x86/xen: allow privcmd hypercalls to be preempted

Hypercalls submitted by user space tools via the privcmd driver can
take a long time (potentially many 10s of seconds) if the hypercall
has many sub-operations.

A fully preemptible kernel may deschedule such as task in any upcall
called from a hypercall continuation.

However, in a kernel with voluntary or no preemption, hypercall
continuations in Xen allow event handlers to be run but the task
issuing the hypercall will not be descheduled until the hypercall is
complete and the ioctl returns to user space.  These long running
tasks may also trigger the kernel's soft lockup detection.

Add xen_preemptible_hcall_begin() and xen_preemptible_hcall_end() to
bracket hypercalls that may be preempted.  Use these in the privcmd
driver.

When returning from an upcall, call xen_maybe_preempt_hcall() which
adds a schedule point if if the current task was within a preemptible
hypercall.

Since _cond_resched() can move the task to a different CPU, clear and
set xen_in_preemptible_hcall around the call.

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
This commit is contained in:
David Vrabel 2015-02-19 15:23:17 +00:00
parent 31795b470b
commit fdfd811ddd
6 changed files with 79 additions and 1 deletions

View File

@ -982,6 +982,9 @@ ENTRY(xen_hypervisor_callback)
ENTRY(xen_do_upcall) ENTRY(xen_do_upcall)
1: mov %esp, %eax 1: mov %esp, %eax
call xen_evtchn_do_upcall call xen_evtchn_do_upcall
#ifndef CONFIG_PREEMPT
call xen_maybe_preempt_hcall
#endif
jmp ret_from_intr jmp ret_from_intr
CFI_ENDPROC CFI_ENDPROC
ENDPROC(xen_hypervisor_callback) ENDPROC(xen_hypervisor_callback)

View File

@ -1208,6 +1208,9 @@ ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs)
popq %rsp popq %rsp
CFI_DEF_CFA_REGISTER rsp CFI_DEF_CFA_REGISTER rsp
decl PER_CPU_VAR(irq_count) decl PER_CPU_VAR(irq_count)
#ifndef CONFIG_PREEMPT
call xen_maybe_preempt_hcall
#endif
jmp error_exit jmp error_exit
CFI_ENDPROC CFI_ENDPROC
END(xen_do_hypervisor_callback) END(xen_do_hypervisor_callback)

View File

@ -2,7 +2,7 @@ ifeq ($(filter y, $(CONFIG_ARM) $(CONFIG_ARM64)),)
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
endif endif
obj-$(CONFIG_X86) += fallback.o obj-$(CONFIG_X86) += fallback.o
obj-y += grant-table.o features.o balloon.o manage.o obj-y += grant-table.o features.o balloon.o manage.o preempt.o
obj-y += events/ obj-y += events/
obj-y += xenbus/ obj-y += xenbus/

44
drivers/xen/preempt.c Normal file
View File

@ -0,0 +1,44 @@
/*
* Preemptible hypercalls
*
* Copyright (C) 2014 Citrix Systems R&D ltd.
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*/
#include <linux/sched.h>
#include <xen/xen-ops.h>
#ifndef CONFIG_PREEMPT
/*
* Some hypercalls issued by the toolstack can take many 10s of
* seconds. Allow tasks running hypercalls via the privcmd driver to
* be voluntarily preempted even if full kernel preemption is
* disabled.
*
* Such preemptible hypercalls are bracketed by
* xen_preemptible_hcall_begin() and xen_preemptible_hcall_end()
* calls.
*/
DEFINE_PER_CPU(bool, xen_in_preemptible_hcall);
EXPORT_SYMBOL_GPL(xen_in_preemptible_hcall);
asmlinkage __visible void xen_maybe_preempt_hcall(void)
{
if (unlikely(__this_cpu_read(xen_in_preemptible_hcall)
&& should_resched())) {
/*
* Clear flag as we may be rescheduled on a different
* cpu.
*/
__this_cpu_write(xen_in_preemptible_hcall, false);
_cond_resched();
__this_cpu_write(xen_in_preemptible_hcall, true);
}
}
#endif /* CONFIG_PREEMPT */

View File

@ -56,10 +56,12 @@ static long privcmd_ioctl_hypercall(void __user *udata)
if (copy_from_user(&hypercall, udata, sizeof(hypercall))) if (copy_from_user(&hypercall, udata, sizeof(hypercall)))
return -EFAULT; return -EFAULT;
xen_preemptible_hcall_begin();
ret = privcmd_call(hypercall.op, ret = privcmd_call(hypercall.op,
hypercall.arg[0], hypercall.arg[1], hypercall.arg[0], hypercall.arg[1],
hypercall.arg[2], hypercall.arg[3], hypercall.arg[2], hypercall.arg[3],
hypercall.arg[4]); hypercall.arg[4]);
xen_preemptible_hcall_end();
return ret; return ret;
} }

View File

@ -46,4 +46,30 @@ static inline efi_system_table_t __init *xen_efi_probe(void)
} }
#endif #endif
#ifdef CONFIG_PREEMPT
static inline void xen_preemptible_hcall_begin(void)
{
}
static inline void xen_preemptible_hcall_end(void)
{
}
#else
DECLARE_PER_CPU(bool, xen_in_preemptible_hcall);
static inline void xen_preemptible_hcall_begin(void)
{
__this_cpu_write(xen_in_preemptible_hcall, true);
}
static inline void xen_preemptible_hcall_end(void)
{
__this_cpu_write(xen_in_preemptible_hcall, false);
}
#endif /* CONFIG_PREEMPT */
#endif /* INCLUDE_XEN_OPS_H */ #endif /* INCLUDE_XEN_OPS_H */