Commit 0645211c43df0b96c51e12980066b3227e10b164

Authored by Jan Kiszka
Committed by Avi Kivity
1 parent 0c106b5aaa

KVM: Switch assigned device IRQ forwarding to threaded handler

This improves the IRQ forwarding for assigned devices: By using the
kernel's threaded IRQ scheme, we can get rid of the latency-prone work
queue and simplify the code in the same run.

Moreover, we no longer have to hold assigned_dev_lock while raising the
guest IRQ, which can be a lenghty operation as we may have to iterate
over all VCPUs. The lock is now only used for synchronizing masking vs.
unmasking of INTx-type IRQs, thus is renames to intx_lock.

Acked-by: Alex Williamson <alex.williamson@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>

Showing 2 changed files with 36 additions and 83 deletions Side-by-side Diff

include/linux/kvm_host.h
... ... @@ -470,16 +470,8 @@
470 470 void (*irq_acked)(struct kvm_irq_ack_notifier *kian);
471 471 };
472 472  
473   -#define KVM_ASSIGNED_MSIX_PENDING 0x1
474   -struct kvm_guest_msix_entry {
475   - u32 vector;
476   - u16 entry;
477   - u16 flags;
478   -};
479   -
480 473 struct kvm_assigned_dev_kernel {
481 474 struct kvm_irq_ack_notifier ack_notifier;
482   - struct work_struct interrupt_work;
483 475 struct list_head list;
484 476 int assigned_dev_id;
485 477 int host_segnr;
486 478  
... ... @@ -490,13 +482,13 @@
490 482 bool host_irq_disabled;
491 483 struct msix_entry *host_msix_entries;
492 484 int guest_irq;
493   - struct kvm_guest_msix_entry *guest_msix_entries;
  485 + struct msix_entry *guest_msix_entries;
494 486 unsigned long irq_requested_type;
495 487 int irq_source_id;
496 488 int flags;
497 489 struct pci_dev *dev;
498 490 struct kvm *kvm;
499   - spinlock_t assigned_dev_lock;
  491 + spinlock_t intx_lock;
500 492 };
501 493  
502 494 struct kvm_irq_mask_notifier {
virt/kvm/assigned-dev.c
... ... @@ -55,58 +55,31 @@
55 55 return index;
56 56 }
57 57  
58   -static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
  58 +static irqreturn_t kvm_assigned_dev_thread(int irq, void *dev_id)
59 59 {
60   - struct kvm_assigned_dev_kernel *assigned_dev;
61   - int i;
  60 + struct kvm_assigned_dev_kernel *assigned_dev = dev_id;
  61 + u32 vector;
  62 + int index;
62 63  
63   - assigned_dev = container_of(work, struct kvm_assigned_dev_kernel,
64   - interrupt_work);
  64 + if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_INTX) {
  65 + spin_lock(&assigned_dev->intx_lock);
  66 + disable_irq_nosync(irq);
  67 + assigned_dev->host_irq_disabled = true;
  68 + spin_unlock(&assigned_dev->intx_lock);
  69 + }
65 70  
66   - spin_lock_irq(&assigned_dev->assigned_dev_lock);
67 71 if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
68   - struct kvm_guest_msix_entry *guest_entries =
69   - assigned_dev->guest_msix_entries;
70   - for (i = 0; i < assigned_dev->entries_nr; i++) {
71   - if (!(guest_entries[i].flags &
72   - KVM_ASSIGNED_MSIX_PENDING))
73   - continue;
74   - guest_entries[i].flags &= ~KVM_ASSIGNED_MSIX_PENDING;
  72 + index = find_index_from_host_irq(assigned_dev, irq);
  73 + if (index >= 0) {
  74 + vector = assigned_dev->
  75 + guest_msix_entries[index].vector;
75 76 kvm_set_irq(assigned_dev->kvm,
76   - assigned_dev->irq_source_id,
77   - guest_entries[i].vector, 1);
  77 + assigned_dev->irq_source_id, vector, 1);
78 78 }
79 79 } else
80 80 kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id,
81 81 assigned_dev->guest_irq, 1);
82 82  
83   - spin_unlock_irq(&assigned_dev->assigned_dev_lock);
84   -}
85   -
86   -static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id)
87   -{
88   - unsigned long flags;
89   - struct kvm_assigned_dev_kernel *assigned_dev =
90   - (struct kvm_assigned_dev_kernel *) dev_id;
91   -
92   - spin_lock_irqsave(&assigned_dev->assigned_dev_lock, flags);
93   - if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
94   - int index = find_index_from_host_irq(assigned_dev, irq);
95   - if (index < 0)
96   - goto out;
97   - assigned_dev->guest_msix_entries[index].flags |=
98   - KVM_ASSIGNED_MSIX_PENDING;
99   - }
100   -
101   - schedule_work(&assigned_dev->interrupt_work);
102   -
103   - if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_GUEST_INTX) {
104   - disable_irq_nosync(irq);
105   - assigned_dev->host_irq_disabled = true;
106   - }
107   -
108   -out:
109   - spin_unlock_irqrestore(&assigned_dev->assigned_dev_lock, flags);
110 83 return IRQ_HANDLED;
111 84 }
112 85  
... ... @@ -114,7 +87,6 @@
114 87 static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian)
115 88 {
116 89 struct kvm_assigned_dev_kernel *dev;
117   - unsigned long flags;
118 90  
119 91 if (kian->gsi == -1)
120 92 return;
121 93  
... ... @@ -127,12 +99,12 @@
127 99 /* The guest irq may be shared so this ack may be
128 100 * from another device.
129 101 */
130   - spin_lock_irqsave(&dev->assigned_dev_lock, flags);
  102 + spin_lock(&dev->intx_lock);
131 103 if (dev->host_irq_disabled) {
132 104 enable_irq(dev->host_irq);
133 105 dev->host_irq_disabled = false;
134 106 }
135   - spin_unlock_irqrestore(&dev->assigned_dev_lock, flags);
  107 + spin_unlock(&dev->intx_lock);
136 108 }
137 109  
138 110 static void deassign_guest_irq(struct kvm *kvm,
139 111  
140 112  
141 113  
142 114  
... ... @@ -155,29 +127,20 @@
155 127 struct kvm_assigned_dev_kernel *assigned_dev)
156 128 {
157 129 /*
158   - * In kvm_free_device_irq, cancel_work_sync return true if:
159   - * 1. work is scheduled, and then cancelled.
160   - * 2. work callback is executed.
  130 + * We disable irq here to prevent further events.
161 131 *
162   - * The first one ensured that the irq is disabled and no more events
163   - * would happen. But for the second one, the irq may be enabled (e.g.
164   - * for MSI). So we disable irq here to prevent further events.
165   - *
166 132 * Notice this maybe result in nested disable if the interrupt type is
167 133 * INTx, but it's OK for we are going to free it.
168 134 *
169 135 * If this function is a part of VM destroy, please ensure that till
170 136 * now, the kvm state is still legal for probably we also have to wait
171   - * interrupt_work done.
  137 + * on a currently running IRQ handler.
172 138 */
173 139 if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
174 140 int i;
175 141 for (i = 0; i < assigned_dev->entries_nr; i++)
176   - disable_irq_nosync(assigned_dev->
177   - host_msix_entries[i].vector);
  142 + disable_irq(assigned_dev->host_msix_entries[i].vector);
178 143  
179   - cancel_work_sync(&assigned_dev->interrupt_work);
180   -
181 144 for (i = 0; i < assigned_dev->entries_nr; i++)
182 145 free_irq(assigned_dev->host_msix_entries[i].vector,
183 146 (void *)assigned_dev);
... ... @@ -188,8 +151,7 @@
188 151 pci_disable_msix(assigned_dev->dev);
189 152 } else {
190 153 /* Deal with MSI and INTx */
191   - disable_irq_nosync(assigned_dev->host_irq);
192   - cancel_work_sync(&assigned_dev->interrupt_work);
  154 + disable_irq(assigned_dev->host_irq);
193 155  
194 156 free_irq(assigned_dev->host_irq, (void *)assigned_dev);
195 157  
... ... @@ -268,8 +230,9 @@
268 230 * on the same interrupt line is not a happy situation: there
269 231 * are going to be long delays in accepting, acking, etc.
270 232 */
271   - if (request_irq(dev->host_irq, kvm_assigned_dev_intr,
272   - 0, "kvm_assigned_intx_device", (void *)dev))
  233 + if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread,
  234 + IRQF_ONESHOT, "kvm_assigned_intx_device",
  235 + (void *)dev))
273 236 return -EIO;
274 237 return 0;
275 238 }
... ... @@ -287,8 +250,8 @@
287 250 }
288 251  
289 252 dev->host_irq = dev->dev->irq;
290   - if (request_irq(dev->host_irq, kvm_assigned_dev_intr, 0,
291   - "kvm_assigned_msi_device", (void *)dev)) {
  253 + if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread,
  254 + 0, "kvm_assigned_msi_device", (void *)dev)) {
292 255 pci_disable_msi(dev->dev);
293 256 return -EIO;
294 257 }
... ... @@ -313,10 +276,10 @@
313 276 return r;
314 277  
315 278 for (i = 0; i < dev->entries_nr; i++) {
316   - r = request_irq(dev->host_msix_entries[i].vector,
317   - kvm_assigned_dev_intr, 0,
318   - "kvm_assigned_msix_device",
319   - (void *)dev);
  279 + r = request_threaded_irq(dev->host_msix_entries[i].vector,
  280 + NULL, kvm_assigned_dev_thread,
  281 + 0, "kvm_assigned_msix_device",
  282 + (void *)dev);
320 283 if (r)
321 284 goto err;
322 285 }
323 286  
... ... @@ -557,12 +520,10 @@
557 520 match->host_devfn = assigned_dev->devfn;
558 521 match->flags = assigned_dev->flags;
559 522 match->dev = dev;
560   - spin_lock_init(&match->assigned_dev_lock);
  523 + spin_lock_init(&match->intx_lock);
561 524 match->irq_source_id = -1;
562 525 match->kvm = kvm;
563 526 match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq;
564   - INIT_WORK(&match->interrupt_work,
565   - kvm_assigned_dev_interrupt_work_handler);
566 527  
567 528 list_add(&match->list, &kvm->arch.assigned_dev_head);
568 529  
... ... @@ -654,9 +615,9 @@
654 615 r = -ENOMEM;
655 616 goto msix_nr_out;
656 617 }
657   - adev->guest_msix_entries = kzalloc(
658   - sizeof(struct kvm_guest_msix_entry) *
659   - entry_nr->entry_nr, GFP_KERNEL);
  618 + adev->guest_msix_entries =
  619 + kzalloc(sizeof(struct msix_entry) * entry_nr->entry_nr,
  620 + GFP_KERNEL);
660 621 if (!adev->guest_msix_entries) {
661 622 kfree(adev->host_msix_entries);
662 623 r = -ENOMEM;