diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2016-10-26 07:35:56 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2016-10-26 08:06:51 -0400 |
commit | 36343f6ea721e003ed11b48a6a05d77a255b3a62 (patch) | |
tree | c32e2452c7269e97ef001eb8f8428a106e9434cf /virt/kvm | |
parent | e1e575f6b026734be3b1f075e780e91ab08ca541 (diff) |
KVM: fix OOPS on flush_work
The conversion done by commit 3706feacd007 ("KVM: Remove deprecated
create_singlethread_workqueue") is broken. It flushes a single work
item &irqfd->shutdown instead of all of them, and even worse if there
is no irqfd on the list then you get a NULL pointer dereference.
Revert the virt/kvm/eventfd.c part of that patch; to avoid the
deprecated function, just allocate our own workqueue---it does
not even have to be unbound---with alloc_workqueue.
Fixes: 3706feacd007
Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'virt/kvm')
-rw-r--r-- | virt/kvm/eventfd.c | 22 | ||||
-rw-r--r-- | virt/kvm/kvm_main.c | 6 |
2 files changed, 25 insertions, 3 deletions
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index f397e9b20370..a29786dd9522 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c | |||
@@ -42,6 +42,7 @@ | |||
42 | 42 | ||
43 | #ifdef CONFIG_HAVE_KVM_IRQFD | 43 | #ifdef CONFIG_HAVE_KVM_IRQFD |
44 | 44 | ||
45 | static struct workqueue_struct *irqfd_cleanup_wq; | ||
45 | 46 | ||
46 | static void | 47 | static void |
47 | irqfd_inject(struct work_struct *work) | 48 | irqfd_inject(struct work_struct *work) |
@@ -167,7 +168,7 @@ irqfd_deactivate(struct kvm_kernel_irqfd *irqfd) | |||
167 | 168 | ||
168 | list_del_init(&irqfd->list); | 169 | list_del_init(&irqfd->list); |
169 | 170 | ||
170 | schedule_work(&irqfd->shutdown); | 171 | queue_work(irqfd_cleanup_wq, &irqfd->shutdown); |
171 | } | 172 | } |
172 | 173 | ||
173 | int __attribute__((weak)) kvm_arch_set_irq_inatomic( | 174 | int __attribute__((weak)) kvm_arch_set_irq_inatomic( |
@@ -554,7 +555,7 @@ kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args) | |||
554 | * so that we guarantee there will not be any more interrupts on this | 555 | * so that we guarantee there will not be any more interrupts on this |
555 | * gsi once this deassign function returns. | 556 | * gsi once this deassign function returns. |
556 | */ | 557 | */ |
557 | flush_work(&irqfd->shutdown); | 558 | flush_workqueue(irqfd_cleanup_wq); |
558 | 559 | ||
559 | return 0; | 560 | return 0; |
560 | } | 561 | } |
@@ -591,7 +592,7 @@ kvm_irqfd_release(struct kvm *kvm) | |||
591 | * Block until we know all outstanding shutdown jobs have completed | 592 | * Block until we know all outstanding shutdown jobs have completed |
592 | * since we do not take a kvm* reference. | 593 | * since we do not take a kvm* reference. |
593 | */ | 594 | */ |
594 | flush_work(&irqfd->shutdown); | 595 | flush_workqueue(irqfd_cleanup_wq); |
595 | 596 | ||
596 | } | 597 | } |
597 | 598 | ||
@@ -621,8 +622,23 @@ void kvm_irq_routing_update(struct kvm *kvm) | |||
621 | spin_unlock_irq(&kvm->irqfds.lock); | 622 | spin_unlock_irq(&kvm->irqfds.lock); |
622 | } | 623 | } |
623 | 624 | ||
625 | /* | ||
626 | * create a host-wide workqueue for issuing deferred shutdown requests | ||
627 | * aggregated from all vm* instances. We need our own isolated | ||
628 | * queue to ease flushing work items when a VM exits. | ||
629 | */ | ||
630 | int kvm_irqfd_init(void) | ||
631 | { | ||
632 | irqfd_cleanup_wq = alloc_workqueue("kvm-irqfd-cleanup", 0, 0); | ||
633 | if (!irqfd_cleanup_wq) | ||
634 | return -ENOMEM; | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
624 | void kvm_irqfd_exit(void) | 639 | void kvm_irqfd_exit(void) |
625 | { | 640 | { |
641 | destroy_workqueue(irqfd_cleanup_wq); | ||
626 | } | 642 | } |
627 | #endif | 643 | #endif |
628 | 644 | ||
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 28510e72618a..d92c3d5b0fbe 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -3846,7 +3846,12 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, | |||
3846 | * kvm_arch_init makes sure there's at most one caller | 3846 | * kvm_arch_init makes sure there's at most one caller |
3847 | * for architectures that support multiple implementations, | 3847 | * for architectures that support multiple implementations, |
3848 | * like intel and amd on x86. | 3848 | * like intel and amd on x86. |
3849 | * kvm_arch_init must be called before kvm_irqfd_init to avoid creating | ||
3850 | * conflicts in case kvm is already setup for another implementation. | ||
3849 | */ | 3851 | */ |
3852 | r = kvm_irqfd_init(); | ||
3853 | if (r) | ||
3854 | goto out_irqfd; | ||
3850 | 3855 | ||
3851 | if (!zalloc_cpumask_var(&cpus_hardware_enabled, GFP_KERNEL)) { | 3856 | if (!zalloc_cpumask_var(&cpus_hardware_enabled, GFP_KERNEL)) { |
3852 | r = -ENOMEM; | 3857 | r = -ENOMEM; |
@@ -3928,6 +3933,7 @@ out_free_0a: | |||
3928 | free_cpumask_var(cpus_hardware_enabled); | 3933 | free_cpumask_var(cpus_hardware_enabled); |
3929 | out_free_0: | 3934 | out_free_0: |
3930 | kvm_irqfd_exit(); | 3935 | kvm_irqfd_exit(); |
3936 | out_irqfd: | ||
3931 | kvm_arch_exit(); | 3937 | kvm_arch_exit(); |
3932 | out_fail: | 3938 | out_fail: |
3933 | return r; | 3939 | return r; |