use smp_send_reschedule in vcpu_kick / assigned dev host intx race fix
cover use smp_send_reschedule in vcpu_kick / assigned dev host intx race fix
PATCH 1/4 qemu: external module: smp_send_reschedule compat
smp_send_reschedule was exported (via smp_ops) in v2.6.24. Create a compat function which schedules the IPI to keventd context, in case interrupts are disabled, for kernels < 2.6.24.
kvm/kernel/external-module-compat-comm.h +8 -0
@@ -116,6 +116,10 @@ int kvm_smp_call_function_single(int cpu, void (*func)(void *info),#endif +#if LINUX_VERSION_CODE <KERNEL_VERSION (2 ,6 ,28 ) +#define nr_cpu_ids NR_CPUS +#endif +#include <linux/miscdevice.h> #ifndef KVM_MINOR #define KVM_MINOR 232
@@ -220,6 +224,10 @@ int kvm_smp_call_function_mask(cpumask_t mask, void (*func) (void *info),#define smp_call_function_mask kvm_smp_call_function_mask +void kvm_smp_send_reschedule (int cpu); + +#define smp_send_reschedule kvm_smp_send_reschedule +#endif /* empty_zero_page isn't exported in all kernels */
kvm/kernel/external-module-compat.c +52 -0
58 lines not on this page · raw message
kvm/kernel/x86/hack-module.awk +2 -0
@@ -35,6 +35,8 @@ BEGIN { split("INIT_WORK desc_struct ldttss_desc64 desc_ptr " \ { sub(/match->dev->msi_enabled/, "kvm_pcidev_msi_enabled(match->dev)") } +{ sub(/smp_send_reschedule/, "kvm_smp_send_reschedule") } + /^static void __vmx_load_host_state/ { vmx_load_host_state = 1 }
Re: [patch 1/4] qemu: external module: smp_send_reschedule compat
kvm/kernel/external-module-compat-comm.h int kvm_smp_call_function_single(int cpu, void (*func)(void *info), · line 122
Quoted 11 lines · [email protected] wrote:
smp_send_reschedulewas exported (viasmp_ops) in v2.6.24.
Create a compat function which schedules the IPI to keventd context, in case interrupts are disabled, for kernels < 2.6.24.#endif
kvm/kernel/external-module-compat-comm.h +4
-0+#if LINUX_VERSION_CODE <KERNEL_VERSION (2 ,6 ,28 ) +#define nr_cpu_ids NR_CPUS +#endif +
Doesn't seem related? Please send as a separate patch.
Quoted 7 lines
kvm/kernel/x86/hack-module.awk +0
-0
kvm/kernel/x86/hack-module.awk BEGIN { split("INIT_WORK desc_struct ldttss_desc64 desc_ptr " \ · line 42
Quoted 8 lines
kvm/kernel/x86/hack-module.awk +2
-0@@ -35,6 +35,8 @@ BEGIN { split("INIT_WORK desc_struct ldttss_desc64 desc_ptr " \ { sub(/match->dev->msi_enabled/, "kvm_pcidev_msi_enabled(match->dev)") } +{ sub(/smp_send_reschedule/, "kvm_smp_send_reschedule") } + /^static void __vmx_load_host_state/ { vmx_load_host_state = 1 }
There's a bit on the top of hack-module.awk that does this slightly simpler. Please also adjust ia64.
This now resides in kvm-kmod.git, please patch against that.
Signature · 1 line
error compiling committee.c: too many arguments to function
PATCH 2/4 KVM: x86: wake up waitqueue before calling get_cpu()
In reply to [email protected]From: Jan Blunck <[email protected]>
This moves the get_cpu() call down to be called after we wake up the waiters. Therefore the waitqueue locks can savely be rt mutex.
arch/x86/kvm/x86.c +2 -1
@@ -4544,7 +4544,7 @@ static void vcpu_kick_intr(void *info)void kvm_vcpu_kick (struct kvm_vcpu *vcpu) {int ipi_pcpu = vcpu->cpu; -int cpu =get_cpu (); +int cpu;if (waitqueue_active (&vcpu->wq)) {wake_up_interruptible (&vcpu->wq);
@@ -4554,6 +4554,7 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu* We may be called synchronously with irqs disabled in guest mode, * So need not to call smp_call_function_single() in that case. */ + cpu =get_cpu ();if (vcpu->guest_mode && vcpu->cpu != cpu)smp_call_function_single (ipi_pcpu, vcpu_kick_intr, vcpu,0 );put_cpu ();
PATCH 3/4 KVM: use smp_send_reschedule in kvm_vcpu_kick
In reply to [email protected]KVM uses a function call IPI to cause the exit of a guest running on a physical cpu. For virtual interrupt notification there is no need to wait on IPI receival, or to execute any function.
This is exactly what the reschedule IPI does, without the overhead of function IPI. So use it instead of smp_call_function_single in kvm_vcpu_kick.
Also change the "guest_mode" variable to a bit in vcpu->requests, and use that to collapse multiple IPI's that would be issued between the first one and zeroing of guest mode.
This allows kvm_vcpu_kick to called with interrupts disabled.
arch/ia64/kernel/irq_ia64.c +3 -0
@@ -610,6 +610,9 @@ static struct irqaction ipi_irqaction = .name = "IPI" }; +/* +* KVM uses this interrupt to force a cpu out of guest mode +*/ static struct irqaction resched_irqaction = { .handler = dummy_handler, .flags =IRQF_DISABLED ,
arch/ia64/kvm/kvm-ia64.c +8 -14
@@ -668,7 +668,7 @@ again: host_ctx =kvm_get_host_context (vcpu); guest_ctx =kvm_get_guest_context (vcpu); - vcpu->guest_mode =1 ; +clear_bit (KVM_REQ_KICK , &vcpu->requests); r =kvm_vcpu_pre_transition (vcpu);if (r <0 )
@@ -685,7 +685,7 @@ again:kvm_vcpu_post_transition (vcpu); vcpu->arch.launched =1 ; - vcpu->guest_mode =0 ; +set_bit (KVM_REQ_KICK , &vcpu->requests);local_irq_enable ();/*
@@ -1884,24 +1884,18 @@ void kvm_arch_hardware_unsetup(void) { } -static void vcpu_kick_intr (void *info) -{ -#ifdef DEBUG -struct kvm_vcpu *vcpu = (struct kvm_vcpu *)info; -printk (KERN_DEBUG "vcpu_kick_intr %p \n" , vcpu); -#endif -} -void kvm_vcpu_kick (struct kvm_vcpu *vcpu) { -int ipi_pcpu = vcpu->cpu; -int cpu =get_cpu (); +int me; +int cpu = vcpu->cpu;if (waitqueue_active (&vcpu->wq))wake_up_interruptible (&vcpu->wq); -if (vcpu->guest_mode && cpu != ipi_pcpu) -smp_call_function_single (ipi_pcpu, vcpu_kick_intr, vcpu,0 ); + me =get_cpu (); +if (cpu != me && (unsigned ) cpu < nr_cpu_ids &&cpu_online (cpu)) +if (!test_and_set_bit (KVM_REQ_KICK , &vcpu->requests)) +smp_send_reschedule (cpu);put_cpu (); }
arch/x86/kernel/smp.c +3 -0
@@ -172,6 +172,9 @@ void smp_reschedule_interrupt(struct pt_ {ack_APIC_irq ();inc_irq_stat (irq_resched_count); +/* +* KVM uses this interrupt to force a cpu out of guest mode +*/ }void smp_call_function_interrupt (struct pt_regs *regs)
arch/x86/kvm/x86.c +11 -25
@@ -3197,6 +3197,9 @@ static int vcpu_enter_guest(struct kvm_vlocal_irq_disable (); +clear_bit (KVM_REQ_KICK , &vcpu->requests); +smp_mb__after_clear_bit (); +if (vcpu->requests ||need_resched () ||signal_pending (current)) {local_irq_enable ();preempt_enable ();
@@ -3204,13 +3207,6 @@ static int vcpu_enter_guest(struct kvm_v goto out; } - vcpu->guest_mode =1 ; -/* -* Make sure that guest_mode assignment won't happen after -* testing the pending IRQ vector bitmap. -*/ -smp_wmb (); -if (vcpu->arch.exception.pending)__queue_exception (vcpu);else if (irqchip_in_kernel (vcpu->kvm))
@@ -3252,7 +3248,7 @@ static int vcpu_enter_guest(struct kvm_vset_debugreg (vcpu->arch.host_dr6,6 );set_debugreg (vcpu->arch.host_dr7,7 ); - vcpu->guest_mode =0 ; +set_bit (KVM_REQ_KICK , &vcpu->requests);local_irq_enable (); ++vcpu->stat.exits;
@@ -4550,30 +4546,20 @@ int kvm_arch_vcpu_runnable(struct kvm_vc || vcpu->arch.nmi_pending; } -static void vcpu_kick_intr (void *info) -{ -#ifdef DEBUG -struct kvm_vcpu *vcpu = (struct kvm_vcpu *)info; -printk (KERN_DEBUG "vcpu_kick_intr %p \n" , vcpu); -#endif -} -void kvm_vcpu_kick (struct kvm_vcpu *vcpu) { -int ipi_pcpu = vcpu->cpu; -int cpu; +int me; +int cpu = vcpu->cpu;if (waitqueue_active (&vcpu->wq)) {wake_up_interruptible (&vcpu->wq); ++vcpu->stat.halt_wakeup; } -/* -* We may be called synchronously with irqs disabled in guest mode, -* So need not to call smp_call_function_single() in that case. -*/ - cpu =get_cpu (); -if (vcpu->guest_mode && vcpu->cpu != cpu) -smp_call_function_single (ipi_pcpu, vcpu_kick_intr, vcpu,0 ); + + me =get_cpu (); +if (cpu != me && (unsigned )cpu < nr_cpu_ids &&cpu_online (cpu)) +if (!test_and_set_bit (KVM_REQ_KICK , &vcpu->requests)) +smp_send_reschedule (cpu);put_cpu (); }
include/linux/kvm_host.h +1 -1
@@ -38,6 +38,7 @@#define KVM_REQ_UNHALT 6#define KVM_REQ_MMU_SYNC 7#define KVM_REQ_KVMCLOCK_UPDATE 8 +#define KVM_REQ_KICK 9#define KVM_USERSPACE_IRQ_SOURCE_ID 0
@@ -72,7 +73,6 @@ struct kvm_vcpu {struct mutex mutex;int cpu;struct kvm_run *run; -int guest_mode;unsigned long requests;unsigned long guest_debug;int fpu_active;
PATCH 4/4 KVM: protect assigned dev workqueue, int handler and irq acker
In reply to [email protected]kvm_assigned_dev_ack_irq is vulnerable to a race condition with the interrupt handler function. It does:
if (dev->host_irq_disabled) {
enable_irq(dev->host_irq);
dev->host_irq_disabled = false;
}If an interrupt triggers before the host->dev_irq_disabled assignment, it will disable the interrupt and set dev->host_irq_disabled to true.
On return to kvm_assigned_dev_ack_irq, dev->host_irq_disabled is set to false, and the next kvm_assigned_dev_ack_irq call will fail to reenable it.
Other than that, having the interrupt handler and work handlers run in parallel sounds like asking for trouble (could not spot any obvious problem, but better not have to, its fragile).
include/linux/kvm_host.h +1 -0
@@ -345,6 +345,7 @@ struct kvm_assigned_dev_kernel {int flags;struct pci_dev *dev;struct kvm *kvm; +spinlock_t assigned_dev_lock; };struct kvm_irq_mask_notifier {
virt/kvm/kvm_main.c +12 -1
@@ -42,6 +42,7 @@#include <linux/mman.h> #include <linux/swap.h> #include <linux/bitops.h> +#include <linux/spinlock.h> #include <asm/processor.h> #include <asm/io.h>
@@ -130,6 +131,7 @@ static void kvm_assigned_dev_interrupt_w* finer-grained lock, update this */ mutex_lock (&kvm->lock); +spin_lock_irq (&assigned_dev->assigned_dev_lock);if (assigned_dev->irq_requested_type &KVM_DEV_IRQ_HOST_MSIX ) {struct kvm_guest_msix_entry *guest_entries = assigned_dev->guest_msix_entries;
@@ -156,18 +158,21 @@ static void kvm_assigned_dev_interrupt_w } } +spin_unlock_irq (&assigned_dev->assigned_dev_lock);mutex_unlock (&assigned_dev->kvm->lock); }static irqreturn_t kvm_assigned_dev_intr (int irq,void *dev_id) { +unsigned long flags;struct kvm_assigned_dev_kernel *assigned_dev = (struct kvm_assigned_dev_kernel *) dev_id; +spin_lock_irqsave (&assigned_dev->assigned_dev_lock, flags);if (assigned_dev->irq_requested_type &KVM_DEV_IRQ_HOST_MSIX ) {int index =find_index_from_host_irq (assigned_dev, irq);if (index <0 ) -return IRQ_HANDLED ; + goto out; assigned_dev->guest_msix_entries[index].flags |=KVM_ASSIGNED_MSIX_PENDING ; }
@@ -177,6 +182,8 @@ static irqreturn_t kvm_assigned_dev_intrdisable_irq_nosync (irq); assigned_dev->host_irq_disabled = true; +out: +spin_unlock_irqrestore (&assigned_dev->assigned_dev_lock, flags);return IRQ_HANDLED ; }
@@ -184,6 +191,7 @@ static irqreturn_t kvm_assigned_dev_intrstatic void kvm_assigned_dev_ack_irq (struct kvm_irq_ack_notifier *kian) {struct kvm_assigned_dev_kernel *dev; +unsigned long flags;if (kian->gsi ==-1 )return ;
@@ -196,10 +204,12 @@ static void kvm_assigned_dev_ack_irq(str/* The guest irq may be shared so this ack may be * from another device. */ +spin_lock_irqsave (&dev->assigned_dev_lock, flags);if (dev->host_irq_disabled) {enable_irq (dev->host_irq); dev->host_irq_disabled = false; } +spin_unlock_irqrestore (&dev->assigned_dev_lock, flags); } static void deassign_guest_irq(struct kvm *kvm,
@@ -615,6 +625,7 @@ static int kvm_vm_ioctl_assign_device(st match->host_devfn = assigned_dev->devfn; match->flags = assigned_dev->flags; match->dev = dev; +spin_lock_init (&match->assigned_dev_lock); match->irq_source_id =-1 ; match->kvm = kvm; match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq;
Ack all. This also solved one bug by my hand. Thanks!
I observe one point: the performance of high workload interrupt(e.g. 10 gigabyte oplin card) dropped dramatically with smp_send_reschedule() method... In one environment(the speed of oplin card also limited by cpu performance), Using smp_call_function_single() can get more than 1G bit/s stably(native got 1.2G), but smp_send_reschedule() can only got around 600M bit/s... And the rescheduling interrupt number is about 2000/second per cpu. And the interrupt rate is about tens of thousands per second for the device.
Anyway, this method is more elegant and correct. Though there is still room for optimize - but of course, the correctness is first priority.
Signature · 2 lines
regards Yang, Sheng
Quoted 10 lines · Sheng Yang
Ack all. This also solved one bug by my hand. Thanks!
I observe one point: the performance of high workload interrupt(e.g. 10 gigabyte oplin card) dropped dramatically with
smp_send_reschedule()method... In one environment(the speed of oplin card also limited by cpu performance), Usingsmp_call_function_single()can get more than 1G bit/s stably(native got 1.2G), butsmp_send_reschedule()can only got around 600M bit/s... And the rescheduling interrupt number is about 2000/second per cpu. And the interrupt rate is about tens of thousands per second for the device.
Quoted 2 lines
Anyway, this method is more elegant and correct. Though there is still room for optimize - but of course, the correctness is first priority.
Are you using the compat code or a kvm.git kernel? Can you remove only the last patch (the spinlock) to confirm its the cause of the slowdown?
Quoted 12 lines · On Thursday 30 April 2009 01:47:57 Marcelo Tosatti wrote:
Quoted 11 lines · Sheng Yang
Ack all. This also solved one bug by my hand. Thanks!
I observe one point: the performance of high workload interrupt(e.g. 10 gigabyte oplin card) dropped dramatically with
smp_send_reschedule()method... In one environment(the speed of oplin card also limited by cpu performance), Usingsmp_call_function_single()can get more than 1G bit/s stably(native got 1.2G), butsmp_send_reschedule()can only got around 600M bit/s... And the rescheduling interrupt number is about 2000/second per cpu. And the interrupt rate is about tens of thousands per second for the device.
Quoted 5 lines
Quoted 2 lines
Anyway, this method is more elegant and correct. Though there is still room for optimize - but of course, the correctness is first priority.
Are you using the compat code or a kvm.git kernel? Can you remove only the last patch (the spinlock) to confirm its the cause of the slowdown?
I am using kvm.git.
I said this because I tried the old version of patch(which have warning) and it would got more than 1G/sec.
I'd like to take a close look at what's happened.
Signature · 2 lines
regards Yang, Sheng
Quoted 19 lines · On Thursday 30 April 2009 08:56:57 Sheng Yang wrote:
Quoted 12 lines · On Thursday 30 April 2009 01:47:57 Marcelo Tosatti wrote:
Quoted 11 lines · Sheng Yang
Ack all. This also solved one bug by my hand. Thanks!
I observe one point: the performance of high workload interrupt(e.g. 10 gigabyte oplin card) dropped dramatically with
smp_send_reschedule()method... In one environment(the speed of oplin card also limited by cpu performance), Usingsmp_call_function_single()can get more than 1G bit/s stably(native got 1.2G), butsmp_send_reschedule()can only got around 600M bit/s... And the rescheduling interrupt number is about 2000/second per cpu. And the interrupt rate is about tens of thousands per second for the device.Quoted 5 lines
Quoted 2 lines
Anyway, this method is more elegant and correct. Though there is still room for optimize - but of course, the correctness is first priority.
Are you using the compat code or a kvm.git kernel? Can you remove only the last patch (the spinlock) to confirm its the cause of the slowdown?
Quoted 6 lines
I am using kvm.git.
I said this because I tried the old version of patch(which have warning) and it would got more than 1G/sec.
I'd like to take a close look at what's happened.
Still ACK this patchset.
And sorry, my memory messed...
The old version of patch and this one offered the same performance. So the problem is not here.
I get more than 1g per second by one of myself's experiment.
Disable/enable irq purposed to use with level interrupt to prevent it send interrupt again after kernel handler return, but it not applied to MSI/MSI-X. Though some interrupt may be merged with one, but AFAIK the driver can handle it well.
My experiment is discard disable/enable IRQ for MSI/MSI-X, then can get much better performance for oplin card, 2x with disable/enable one.
I would prepare a patch for it.
Signature · 2 lines
regards Yang, Sheng
Quoted 43 lines · On Thursday 30 April 2009 09:59:56 Sheng Yang wrote:
Quoted 21 lines · On Thursday 30 April 2009 08:56:57 Sheng Yang wrote:
Quoted 13 lines · On Thursday 30 April 2009 01:47:57 Marcelo Tosatti wrote:
Quoted 12 lines · Sheng Yang
Ack all. This also solved one bug by my hand. Thanks!
I observe one point: the performance of high workload interrupt(e.g. 10 gigabyte oplin card) dropped dramatically with
smp_send_reschedule()method... In one environment(the speed of oplin card also limited by cpu performance), Usingsmp_call_function_single()can get more than 1G bit/s stably(native got 1.2G), butsmp_send_reschedule()can only got around 600M bit/s... And the rescheduling interrupt number is about 2000/second per cpu. And the interrupt rate is about tens of thousands per second for the device.Quoted 6 lines
Quoted 3 lines
Anyway, this method is more elegant and correct. Though there is still room for optimize - but of course, the correctness is first priority.
Are you using the compat code or a kvm.git kernel? Can you remove only the last patch (the spinlock) to confirm its the cause of the slowdown?
Quoted 6 lines
I am using kvm.git.
I said this because I tried the old version of patch(which have warning) and it would got more than 1G/sec.
I'd like to take a close look at what's happened.
Still ACK this patchset.
And sorry, my memory messed...
The old version of patch and this one offered the same performance. So the problem is not here.
I get more than 1g per second by one of myself's experiment.
Disable/enable irq purposed to use with level interrupt to prevent it send interrupt again after kernel handler return, but it not applied to MSI/MSI-X. Though some interrupt may be merged with one, but AFAIK the driver can handle it well.
Quoted 4 lines
My experiment is discard disable/enable IRQ for MSI/MSI-X, then can get much better performance for oplin card, 2x with disable/enable one.
I would prepare a patch for it.
Hi Avi
Is there any issue blocked this patchset?
Thanks!
Signature · 2 lines
regards Yang, Sheng
Quoted 2 lines · Sheng Yang
Is there any issue blocked this patchset?
Yes, a slow maintainer. I'll go review it now.
Signature · 1 line
error compiling committee.c: too many arguments to function
cover smp_send_reschedule / assigned dev host intx race v2
Addressing comments.
PATCH 1/4 kvm-kmod: nr_cpu_ids compat
external-module-compat-comm.h +4 -0
@@ -116,6 +116,10 @@ int kvm_smp_call_function_single(int cpu#endif +#if LINUX_VERSION_CODE <KERNEL_VERSION (2 ,6 ,28 ) +#define nr_cpu_ids NR_CPUS +#endif +#include <linux/miscdevice.h> #ifndef KVM_MINOR #define KVM_MINOR 232
PATCH 2/4 kvm-kmod: smp_send_reschedule compat
In reply to [email protected]smp_send_reschedule was exported (via smp_ops) in v2.6.24. Create a compat function which schedules the IPI to keventd context, in case interrupts are disabled, for kernels < 2.6.24.
ia64/hack-module.awk +1 -1
@@ -1,4 +1,4 @@ -BEGIN { split("INIT_WORK on_each_cpu smp_call_function " \ +BEGIN { split("INIT_WORK on_each_cpu smp_call_function smp_send_reschedule " \ "hrtimer_add_expires_ns hrtimer_get_expires " \ "hrtimer_get_expires_ns hrtimer_start_expires " \ "hrtimer_expires_remaining " \
x86/hack-module.awk +1 -1
@@ -1,7 +1,7 @@ BEGIN { split("INIT_WORK desc_struct ldttss_desc64 desc_ptr " \ "hrtimer_add_expires_ns hrtimer_get_expires " \ "hrtimer_get_expires_ns hrtimer_start_expires " \ - "hrtimer_expires_remaining " \ + "hrtimer_expires_remaining smp_send_reschedule " \ "on_each_cpu relay_open request_irq free_irq" , compat_apis); } /^int kvm_init\(/ { anon_inodes = 1 }
external-module-compat-comm.h +4 -0
@@ -224,6 +224,10 @@ int kvm_smp_call_function_mask(cpumask_t#define smp_call_function_mask kvm_smp_call_function_mask +void kvm_smp_send_reschedule (int cpu); + +#define smp_send_reschedule kvm_smp_send_reschedule +#endif /* empty_zero_page isn't exported in all kernels */
external-module-compat.c +52 -0
58 lines not on this page · raw message
PATCH 3/4 KVM: use smp_send_reschedule in kvm_vcpu_kick
In reply to [email protected]KVM uses a function call IPI to cause the exit of a guest running on a physical cpu. For virtual interrupt notification there is no need to wait on IPI receival, or to execute any function.
This is exactly what the reschedule IPI does, without the overhead of function IPI. So use it instead of smp_call_function_single in kvm_vcpu_kick.
Also change the "guest_mode" variable to a bit in vcpu->requests, and use that to collapse multiple IPI's that would be issued between the first one and zeroing of guest mode.
This allows kvm_vcpu_kick to called with interrupts disabled.
arch/ia64/kernel/irq_ia64.c +3 -0
@@ -610,6 +610,9 @@ static struct irqaction ipi_irqaction = .name = "IPI" }; +/* +* KVM uses this interrupt to force a cpu out of guest mode +*/ static struct irqaction resched_irqaction = { .handler = dummy_handler, .flags =IRQF_DISABLED ,
arch/ia64/kvm/kvm-ia64.c +8 -14
@@ -668,7 +668,7 @@ again: host_ctx =kvm_get_host_context (vcpu); guest_ctx =kvm_get_guest_context (vcpu); - vcpu->guest_mode =1 ; +clear_bit (KVM_REQ_KICK , &vcpu->requests); r =kvm_vcpu_pre_transition (vcpu);if (r <0 )
@@ -685,7 +685,7 @@ again:kvm_vcpu_post_transition (vcpu); vcpu->arch.launched =1 ; - vcpu->guest_mode =0 ; +set_bit (KVM_REQ_KICK , &vcpu->requests);local_irq_enable ();/*
@@ -1879,24 +1879,18 @@ void kvm_arch_hardware_unsetup(void) { } -static void vcpu_kick_intr (void *info) -{ -#ifdef DEBUG -struct kvm_vcpu *vcpu = (struct kvm_vcpu *)info; -printk (KERN_DEBUG "vcpu_kick_intr %p \n" , vcpu); -#endif -} -void kvm_vcpu_kick (struct kvm_vcpu *vcpu) { -int ipi_pcpu = vcpu->cpu; -int cpu =get_cpu (); +int me; +int cpu = vcpu->cpu;if (waitqueue_active (&vcpu->wq))wake_up_interruptible (&vcpu->wq); -if (vcpu->guest_mode && cpu != ipi_pcpu) -smp_call_function_single (ipi_pcpu, vcpu_kick_intr, vcpu,0 ); + me =get_cpu (); +if (cpu != me && (unsigned ) cpu < nr_cpu_ids &&cpu_online (cpu)) +if (!test_and_set_bit (KVM_REQ_KICK , &vcpu->requests)) +smp_send_reschedule (cpu);put_cpu (); }
arch/x86/kernel/smp.c +3 -0
@@ -172,6 +172,9 @@ void smp_reschedule_interrupt(struct pt_ {ack_APIC_irq ();inc_irq_stat (irq_resched_count); +/* +* KVM uses this interrupt to force a cpu out of guest mode +*/ }void smp_call_function_interrupt (struct pt_regs *regs)
arch/x86/kvm/x86.c +11 -25
@@ -3259,6 +3259,9 @@ static int vcpu_enter_guest(struct kvm_vlocal_irq_disable (); +clear_bit (KVM_REQ_KICK , &vcpu->requests); +smp_mb__after_clear_bit (); +if (vcpu->requests ||need_resched () ||signal_pending (current)) {local_irq_enable ();preempt_enable ();
@@ -3266,13 +3269,6 @@ static int vcpu_enter_guest(struct kvm_v goto out; } - vcpu->guest_mode =1 ; -/* -* Make sure that guest_mode assignment won't happen after -* testing the pending IRQ vector bitmap. -*/ -smp_wmb (); -if (vcpu->arch.exception.pending)__queue_exception (vcpu);else
@@ -3317,7 +3313,7 @@ static int vcpu_enter_guest(struct kvm_vset_debugreg (vcpu->arch.host_dr6,6 );set_debugreg (vcpu->arch.host_dr7,7 ); - vcpu->guest_mode =0 ; +set_bit (KVM_REQ_KICK , &vcpu->requests);local_irq_enable (); ++vcpu->stat.exits;
@@ -4611,30 +4607,20 @@ int kvm_arch_vcpu_runnable(struct kvm_vc || vcpu->arch.nmi_pending; } -static void vcpu_kick_intr (void *info) -{ -#ifdef DEBUG -struct kvm_vcpu *vcpu = (struct kvm_vcpu *)info; -printk (KERN_DEBUG "vcpu_kick_intr %p \n" , vcpu); -#endif -} -void kvm_vcpu_kick (struct kvm_vcpu *vcpu) { -int ipi_pcpu = vcpu->cpu; -int cpu; +int me; +int cpu = vcpu->cpu;if (waitqueue_active (&vcpu->wq)) {wake_up_interruptible (&vcpu->wq); ++vcpu->stat.halt_wakeup; } -/* -* We may be called synchronously with irqs disabled in guest mode, -* So need not to call smp_call_function_single() in that case. -*/ - cpu =get_cpu (); -if (vcpu->guest_mode && vcpu->cpu != cpu) -smp_call_function_single (ipi_pcpu, vcpu_kick_intr, vcpu,0 ); + + me =get_cpu (); +if (cpu != me && (unsigned )cpu < nr_cpu_ids &&cpu_online (cpu)) +if (!test_and_set_bit (KVM_REQ_KICK , &vcpu->requests)) +smp_send_reschedule (cpu);put_cpu (); }
include/linux/kvm_host.h +1 -1
@@ -38,6 +38,7 @@#define KVM_REQ_UNHALT 6#define KVM_REQ_MMU_SYNC 7#define KVM_REQ_KVMCLOCK_UPDATE 8 +#define KVM_REQ_KICK 9#define KVM_USERSPACE_IRQ_SOURCE_ID 0
@@ -72,7 +73,6 @@ struct kvm_vcpu {struct mutex mutex;int cpu;struct kvm_run *run; -int guest_mode;unsigned long requests;unsigned long guest_debug;int fpu_active;
Re: [patch 3/4] KVM: use smp_send_reschedule in kvm_vcpu_kick
Quoted 8 lines · On Thu, May 07, 2009 at 05:55:12PM -0300, [email protected] wrote:
KVM uses a function call IPI to cause the exit of a guest running on a physical cpu. For virtual interrupt notification there is no need to wait on IPI receival, or to execute any function.
This is exactly what the reschedule IPI does, without the overhead of function IPI. So use it instead of
smp_call_function_singleinkvm_vcpu_kick.
Quoted 8 lines
Also change the "
guest_mode" variable to a bit invcpu->requests, and use that to collapse multiple IPI's that would be issued between the first one and zeroing of guest mode.This allows
kvm_vcpu_kickto called with interrupts disabled.Signed-off-by Marcelo Tosatti <[email protected]>
Except one small nitpick below.
Quoted 15 lines
Index:
kvm-pending/arch/x86/kvm/x86.c
===================================================================arch/x86/kvm/x86.c +3
-0@@ -3259,6 +3259,9 @@ static int vcpu_enter_guest(struct kvm_vlocal_irq_disable (); +clear_bit (KVM_REQ_KICK , &vcpu->requests); +smp_mb__after_clear_bit (); +if (vcpu->requests ||need_resched () ||signal_pending (current)) {local_irq_enable ();preempt_enable ();
arch/x86/kvm/x86.c static int vcpu_enter_guest(struct kvm_v · line 3275
Quoted 10 lines
arch/x86/kvm/x86.c +0
-7@@ -3266,13 +3269,6 @@ static int vcpu_enter_guest(struct kvm_v goto out; } - vcpu->guest_mode =1 ; -/* -* Make sure that guest_mode assignment won't happen after -* testing the pending IRQ vector bitmap. -*/ -smp_wmb (); -
Can you leave this comment. I like it :)
--
Gleb.
PATCH 4/4 KVM: protect assigned dev workqueue, int handler and irq acker
In reply to [email protected]kvm_assigned_dev_ack_irq is vulnerable to a race condition with the interrupt handler function. It does:
if (dev->host_irq_disabled) {
enable_irq(dev->host_irq);
dev->host_irq_disabled = false;
}If an interrupt triggers before the host->dev_irq_disabled assignment, it will disable the interrupt and set dev->host_irq_disabled to true.
On return to kvm_assigned_dev_ack_irq, dev->host_irq_disabled is set to false, and the next kvm_assigned_dev_ack_irq call will fail to reenable it.
Other than that, having the interrupt handler and work handlers run in parallel sounds like asking for trouble (could not spot any obvious problem, but better not have to, its fragile).
include/linux/kvm_host.h +1 -0
@@ -346,6 +346,7 @@ struct kvm_assigned_dev_kernel {int flags;struct pci_dev *dev;struct kvm *kvm; +spinlock_t assigned_dev_lock; };struct kvm_irq_mask_notifier {
virt/kvm/kvm_main.c +12 -1
@@ -42,6 +42,7 @@#include <linux/mman.h> #include <linux/swap.h> #include <linux/bitops.h> +#include <linux/spinlock.h> #include <asm/processor.h> #include <asm/io.h>
@@ -130,6 +131,7 @@ static void kvm_assigned_dev_interrupt_w* finer-grained lock, update this */ mutex_lock (&kvm->lock); +spin_lock_irq (&assigned_dev->assigned_dev_lock);if (assigned_dev->irq_requested_type &KVM_DEV_IRQ_HOST_MSIX ) {struct kvm_guest_msix_entry *guest_entries = assigned_dev->guest_msix_entries;
@@ -156,18 +158,21 @@ static void kvm_assigned_dev_interrupt_w } } +spin_unlock_irq (&assigned_dev->assigned_dev_lock);mutex_unlock (&assigned_dev->kvm->lock); }static irqreturn_t kvm_assigned_dev_intr (int irq,void *dev_id) { +unsigned long flags;struct kvm_assigned_dev_kernel *assigned_dev = (struct kvm_assigned_dev_kernel *) dev_id; +spin_lock_irqsave (&assigned_dev->assigned_dev_lock, flags);if (assigned_dev->irq_requested_type &KVM_DEV_IRQ_HOST_MSIX ) {int index =find_index_from_host_irq (assigned_dev, irq);if (index <0 ) -return IRQ_HANDLED ; + goto out; assigned_dev->guest_msix_entries[index].flags |=KVM_ASSIGNED_MSIX_PENDING ; }
@@ -177,6 +182,8 @@ static irqreturn_t kvm_assigned_dev_intrdisable_irq_nosync (irq); assigned_dev->host_irq_disabled = true; +out: +spin_unlock_irqrestore (&assigned_dev->assigned_dev_lock, flags);return IRQ_HANDLED ; }
@@ -184,6 +191,7 @@ static irqreturn_t kvm_assigned_dev_intrstatic void kvm_assigned_dev_ack_irq (struct kvm_irq_ack_notifier *kian) {struct kvm_assigned_dev_kernel *dev; +unsigned long flags;if (kian->gsi ==-1 )return ;
@@ -196,10 +204,12 @@ static void kvm_assigned_dev_ack_irq(str/* The guest irq may be shared so this ack may be * from another device. */ +spin_lock_irqsave (&dev->assigned_dev_lock, flags);if (dev->host_irq_disabled) {enable_irq (dev->host_irq); dev->host_irq_disabled = false; } +spin_unlock_irqrestore (&dev->assigned_dev_lock, flags); } static void deassign_guest_irq(struct kvm *kvm,
@@ -615,6 +625,7 @@ static int kvm_vm_ioctl_assign_device(st match->host_devfn = assigned_dev->devfn; match->flags = assigned_dev->flags; match->dev = dev; +spin_lock_init (&match->assigned_dev_lock); match->irq_source_id =-1 ; match->kvm = kvm; match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq;
Avi Kivity
AppliedRe: [patch 0/4] smp_send_reschedule / assigned dev host intx race v2
In reply to [email protected]Quoted 4 lines · [email protected] wrote:
Addressing comments.
Applied, thanks.
Signature · 1 line
error compiling committee.c: too many arguments to function