diff options
| author | Marcelo Tosatti <mtosatti@redhat.com> | 2009-06-04 14:08:24 -0400 |
|---|---|---|
| committer | Avi Kivity <avi@redhat.com> | 2009-09-10 01:32:49 -0400 |
| commit | fa40a8214bb9bcae8d49c234c19d8b4a6c1f37ff (patch) | |
| tree | 6449f27072f128a1c39faaaeef1787f754345aaf /virt | |
| parent | 60eead79ad8750f80384cbe48fc44edcc78a0305 (diff) | |
KVM: switch irq injection/acking data structures to irq_lock
Protect irq injection/acking data structures with a separate irq_lock
mutex. This fixes the following deadlock:
CPU A CPU B
kvm_vm_ioctl_deassign_dev_irq()
mutex_lock(&kvm->lock); worker_thread()
-> kvm_deassign_irq() -> kvm_assigned_dev_interrupt_work_handler()
-> deassign_host_irq() mutex_lock(&kvm->lock);
-> cancel_work_sync() [blocked]
[gleb: fix ia64 path]
Reported-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'virt')
| -rw-r--r-- | virt/kvm/eventfd.c | 4 | ||||
| -rw-r--r-- | virt/kvm/irq_comm.c | 34 | ||||
| -rw-r--r-- | virt/kvm/kvm_main.c | 16 |
3 files changed, 39 insertions, 15 deletions
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 314012323afe..4092b8dcd510 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c | |||
| @@ -57,10 +57,10 @@ irqfd_inject(struct work_struct *work) | |||
| 57 | struct _irqfd *irqfd = container_of(work, struct _irqfd, inject); | 57 | struct _irqfd *irqfd = container_of(work, struct _irqfd, inject); |
| 58 | struct kvm *kvm = irqfd->kvm; | 58 | struct kvm *kvm = irqfd->kvm; |
| 59 | 59 | ||
| 60 | mutex_lock(&kvm->lock); | 60 | mutex_lock(&kvm->irq_lock); |
| 61 | kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 1); | 61 | kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 1); |
| 62 | kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 0); | 62 | kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 0); |
| 63 | mutex_unlock(&kvm->lock); | 63 | mutex_unlock(&kvm->irq_lock); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | /* | 66 | /* |
diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c index ddc17f0e2f35..08a9a49481b2 100644 --- a/virt/kvm/irq_comm.c +++ b/virt/kvm/irq_comm.c | |||
| @@ -62,6 +62,8 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, | |||
| 62 | int i, r = -1; | 62 | int i, r = -1; |
| 63 | struct kvm_vcpu *vcpu, *lowest = NULL; | 63 | struct kvm_vcpu *vcpu, *lowest = NULL; |
| 64 | 64 | ||
| 65 | WARN_ON(!mutex_is_locked(&kvm->irq_lock)); | ||
| 66 | |||
| 65 | if (irq->dest_mode == 0 && irq->dest_id == 0xff && | 67 | if (irq->dest_mode == 0 && irq->dest_id == 0xff && |
| 66 | kvm_is_dm_lowest_prio(irq)) | 68 | kvm_is_dm_lowest_prio(irq)) |
| 67 | printk(KERN_INFO "kvm: apic: phys broadcast and lowest prio\n"); | 69 | printk(KERN_INFO "kvm: apic: phys broadcast and lowest prio\n"); |
| @@ -113,7 +115,7 @@ static int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, | |||
| 113 | return kvm_irq_delivery_to_apic(kvm, NULL, &irq); | 115 | return kvm_irq_delivery_to_apic(kvm, NULL, &irq); |
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | /* This should be called with the kvm->lock mutex held | 118 | /* This should be called with the kvm->irq_lock mutex held |
| 117 | * Return value: | 119 | * Return value: |
| 118 | * < 0 Interrupt was ignored (masked or not delivered for other reasons) | 120 | * < 0 Interrupt was ignored (masked or not delivered for other reasons) |
| 119 | * = 0 Interrupt was coalesced (previous irq is still pending) | 121 | * = 0 Interrupt was coalesced (previous irq is still pending) |
| @@ -125,6 +127,8 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level) | |||
| 125 | unsigned long *irq_state, sig_level; | 127 | unsigned long *irq_state, sig_level; |
| 126 | int ret = -1; | 128 | int ret = -1; |
| 127 | 129 | ||
| 130 | WARN_ON(!mutex_is_locked(&kvm->irq_lock)); | ||
| 131 | |||
| 128 | if (irq < KVM_IOAPIC_NUM_PINS) { | 132 | if (irq < KVM_IOAPIC_NUM_PINS) { |
| 129 | irq_state = (unsigned long *)&kvm->arch.irq_states[irq]; | 133 | irq_state = (unsigned long *)&kvm->arch.irq_states[irq]; |
| 130 | 134 | ||
| @@ -175,19 +179,26 @@ void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin) | |||
| 175 | void kvm_register_irq_ack_notifier(struct kvm *kvm, | 179 | void kvm_register_irq_ack_notifier(struct kvm *kvm, |
| 176 | struct kvm_irq_ack_notifier *kian) | 180 | struct kvm_irq_ack_notifier *kian) |
| 177 | { | 181 | { |
| 182 | mutex_lock(&kvm->irq_lock); | ||
| 178 | hlist_add_head(&kian->link, &kvm->arch.irq_ack_notifier_list); | 183 | hlist_add_head(&kian->link, &kvm->arch.irq_ack_notifier_list); |
| 184 | mutex_unlock(&kvm->irq_lock); | ||
| 179 | } | 185 | } |
| 180 | 186 | ||
| 181 | void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian) | 187 | void kvm_unregister_irq_ack_notifier(struct kvm *kvm, |
| 188 | struct kvm_irq_ack_notifier *kian) | ||
| 182 | { | 189 | { |
| 190 | mutex_lock(&kvm->irq_lock); | ||
| 183 | hlist_del_init(&kian->link); | 191 | hlist_del_init(&kian->link); |
| 192 | mutex_unlock(&kvm->irq_lock); | ||
| 184 | } | 193 | } |
| 185 | 194 | ||
| 186 | /* The caller must hold kvm->lock mutex */ | ||
| 187 | int kvm_request_irq_source_id(struct kvm *kvm) | 195 | int kvm_request_irq_source_id(struct kvm *kvm) |
| 188 | { | 196 | { |
| 189 | unsigned long *bitmap = &kvm->arch.irq_sources_bitmap; | 197 | unsigned long *bitmap = &kvm->arch.irq_sources_bitmap; |
| 190 | int irq_source_id = find_first_zero_bit(bitmap, | 198 | int irq_source_id; |
| 199 | |||
| 200 | mutex_lock(&kvm->irq_lock); | ||
| 201 | irq_source_id = find_first_zero_bit(bitmap, | ||
| 191 | sizeof(kvm->arch.irq_sources_bitmap)); | 202 | sizeof(kvm->arch.irq_sources_bitmap)); |
| 192 | 203 | ||
| 193 | if (irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) { | 204 | if (irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) { |
| @@ -197,6 +208,7 @@ int kvm_request_irq_source_id(struct kvm *kvm) | |||
| 197 | 208 | ||
| 198 | ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); | 209 | ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); |
| 199 | set_bit(irq_source_id, bitmap); | 210 | set_bit(irq_source_id, bitmap); |
| 211 | mutex_unlock(&kvm->irq_lock); | ||
| 200 | 212 | ||
| 201 | return irq_source_id; | 213 | return irq_source_id; |
| 202 | } | 214 | } |
| @@ -207,6 +219,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id) | |||
| 207 | 219 | ||
| 208 | ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); | 220 | ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID); |
| 209 | 221 | ||
| 222 | mutex_lock(&kvm->irq_lock); | ||
| 210 | if (irq_source_id < 0 || | 223 | if (irq_source_id < 0 || |
| 211 | irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) { | 224 | irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) { |
| 212 | printk(KERN_ERR "kvm: IRQ source ID out of range!\n"); | 225 | printk(KERN_ERR "kvm: IRQ source ID out of range!\n"); |
| @@ -215,19 +228,24 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id) | |||
| 215 | for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++) | 228 | for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++) |
| 216 | clear_bit(irq_source_id, &kvm->arch.irq_states[i]); | 229 | clear_bit(irq_source_id, &kvm->arch.irq_states[i]); |
| 217 | clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap); | 230 | clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap); |
| 231 | mutex_unlock(&kvm->irq_lock); | ||
| 218 | } | 232 | } |
| 219 | 233 | ||
| 220 | void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, | 234 | void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, |
| 221 | struct kvm_irq_mask_notifier *kimn) | 235 | struct kvm_irq_mask_notifier *kimn) |
| 222 | { | 236 | { |
| 237 | mutex_lock(&kvm->irq_lock); | ||
| 223 | kimn->irq = irq; | 238 | kimn->irq = irq; |
| 224 | hlist_add_head(&kimn->link, &kvm->mask_notifier_list); | 239 | hlist_add_head(&kimn->link, &kvm->mask_notifier_list); |
| 240 | mutex_unlock(&kvm->irq_lock); | ||
| 225 | } | 241 | } |
| 226 | 242 | ||
| 227 | void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, | 243 | void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, |
| 228 | struct kvm_irq_mask_notifier *kimn) | 244 | struct kvm_irq_mask_notifier *kimn) |
| 229 | { | 245 | { |
| 246 | mutex_lock(&kvm->irq_lock); | ||
| 230 | hlist_del(&kimn->link); | 247 | hlist_del(&kimn->link); |
| 248 | mutex_unlock(&kvm->irq_lock); | ||
| 231 | } | 249 | } |
| 232 | 250 | ||
| 233 | void kvm_fire_mask_notifiers(struct kvm *kvm, int irq, bool mask) | 251 | void kvm_fire_mask_notifiers(struct kvm *kvm, int irq, bool mask) |
| @@ -235,6 +253,8 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, int irq, bool mask) | |||
| 235 | struct kvm_irq_mask_notifier *kimn; | 253 | struct kvm_irq_mask_notifier *kimn; |
| 236 | struct hlist_node *n; | 254 | struct hlist_node *n; |
| 237 | 255 | ||
| 256 | WARN_ON(!mutex_is_locked(&kvm->irq_lock)); | ||
| 257 | |||
| 238 | hlist_for_each_entry(kimn, n, &kvm->mask_notifier_list, link) | 258 | hlist_for_each_entry(kimn, n, &kvm->mask_notifier_list, link) |
| 239 | if (kimn->irq == irq) | 259 | if (kimn->irq == irq) |
| 240 | kimn->func(kimn, mask); | 260 | kimn->func(kimn, mask); |
| @@ -250,7 +270,9 @@ static void __kvm_free_irq_routing(struct list_head *irq_routing) | |||
| 250 | 270 | ||
| 251 | void kvm_free_irq_routing(struct kvm *kvm) | 271 | void kvm_free_irq_routing(struct kvm *kvm) |
| 252 | { | 272 | { |
| 273 | mutex_lock(&kvm->irq_lock); | ||
| 253 | __kvm_free_irq_routing(&kvm->irq_routing); | 274 | __kvm_free_irq_routing(&kvm->irq_routing); |
| 275 | mutex_unlock(&kvm->irq_lock); | ||
| 254 | } | 276 | } |
| 255 | 277 | ||
| 256 | static int setup_routing_entry(struct kvm_kernel_irq_routing_entry *e, | 278 | static int setup_routing_entry(struct kvm_kernel_irq_routing_entry *e, |
| @@ -325,13 +347,13 @@ int kvm_set_irq_routing(struct kvm *kvm, | |||
| 325 | e = NULL; | 347 | e = NULL; |
| 326 | } | 348 | } |
| 327 | 349 | ||
| 328 | mutex_lock(&kvm->lock); | 350 | mutex_lock(&kvm->irq_lock); |
| 329 | list_splice(&kvm->irq_routing, &tmp); | 351 | list_splice(&kvm->irq_routing, &tmp); |
| 330 | INIT_LIST_HEAD(&kvm->irq_routing); | 352 | INIT_LIST_HEAD(&kvm->irq_routing); |
| 331 | list_splice(&irq_list, &kvm->irq_routing); | 353 | list_splice(&irq_list, &kvm->irq_routing); |
| 332 | INIT_LIST_HEAD(&irq_list); | 354 | INIT_LIST_HEAD(&irq_list); |
| 333 | list_splice(&tmp, &irq_list); | 355 | list_splice(&tmp, &irq_list); |
| 334 | mutex_unlock(&kvm->lock); | 356 | mutex_unlock(&kvm->irq_lock); |
| 335 | 357 | ||
| 336 | r = 0; | 358 | r = 0; |
| 337 | 359 | ||
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index d47e660fb709..0d481b282448 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
| @@ -62,6 +62,12 @@ | |||
| 62 | MODULE_AUTHOR("Qumranet"); | 62 | MODULE_AUTHOR("Qumranet"); |
| 63 | MODULE_LICENSE("GPL"); | 63 | MODULE_LICENSE("GPL"); |
| 64 | 64 | ||
| 65 | /* | ||
| 66 | * Ordering of locks: | ||
| 67 | * | ||
| 68 | * kvm->lock --> kvm->irq_lock | ||
| 69 | */ | ||
| 70 | |||
| 65 | DEFINE_SPINLOCK(kvm_lock); | 71 | DEFINE_SPINLOCK(kvm_lock); |
| 66 | LIST_HEAD(vm_list); | 72 | LIST_HEAD(vm_list); |
| 67 | 73 | ||
| @@ -126,11 +132,7 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work) | |||
| 126 | interrupt_work); | 132 | interrupt_work); |
| 127 | kvm = assigned_dev->kvm; | 133 | kvm = assigned_dev->kvm; |
| 128 | 134 | ||
| 129 | /* This is taken to safely inject irq inside the guest. When | 135 | mutex_lock(&kvm->irq_lock); |
| 130 | * the interrupt injection (or the ioapic code) uses a | ||
| 131 | * finer-grained lock, update this | ||
| 132 | */ | ||
| 133 | mutex_lock(&kvm->lock); | ||
| 134 | spin_lock_irq(&assigned_dev->assigned_dev_lock); | 136 | spin_lock_irq(&assigned_dev->assigned_dev_lock); |
| 135 | if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) { | 137 | if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) { |
| 136 | struct kvm_guest_msix_entry *guest_entries = | 138 | struct kvm_guest_msix_entry *guest_entries = |
| @@ -149,7 +151,7 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work) | |||
| 149 | assigned_dev->guest_irq, 1); | 151 | assigned_dev->guest_irq, 1); |
| 150 | 152 | ||
| 151 | spin_unlock_irq(&assigned_dev->assigned_dev_lock); | 153 | spin_unlock_irq(&assigned_dev->assigned_dev_lock); |
| 152 | mutex_unlock(&assigned_dev->kvm->lock); | 154 | mutex_unlock(&assigned_dev->kvm->irq_lock); |
| 153 | } | 155 | } |
| 154 | 156 | ||
| 155 | static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id) | 157 | static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id) |
| @@ -207,7 +209,7 @@ static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian) | |||
| 207 | static void deassign_guest_irq(struct kvm *kvm, | 209 | static void deassign_guest_irq(struct kvm *kvm, |
| 208 | struct kvm_assigned_dev_kernel *assigned_dev) | 210 | struct kvm_assigned_dev_kernel *assigned_dev) |
| 209 | { | 211 | { |
| 210 | kvm_unregister_irq_ack_notifier(&assigned_dev->ack_notifier); | 212 | kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier); |
| 211 | assigned_dev->ack_notifier.gsi = -1; | 213 | assigned_dev->ack_notifier.gsi = -1; |
| 212 | 214 | ||
| 213 | if (assigned_dev->irq_source_id != -1) | 215 | if (assigned_dev->irq_source_id != -1) |
