aboutsummaryrefslogtreecommitdiffstats
path: root/virt/kvm/kvm_main.c
diff options
context:
space:
mode:
authorSheng Yang <sheng@linux.intel.com>2009-02-25 04:22:28 -0500
committerAvi Kivity <avi@redhat.com>2009-06-10 04:48:23 -0400
commitd510d6cc653bc4b3094ea73afe12600d0ab445b3 (patch)
tree60ea170465dcddcd5cdd7c29a29df3f28407c202 /virt/kvm/kvm_main.c
parent2350bd1f62c8706c22b8e58c3bfff10806c0a31b (diff)
KVM: Enable MSI-X for KVM assigned device
This patch finally enable MSI-X. What we need for MSI-X: 1. Intercept one page in MMIO region of device. So that we can get guest desired MSI-X table and set up the real one. Now this have been done by guest, and transfer to kernel using ioctl KVM_SET_MSIX_NR and KVM_SET_MSIX_ENTRY. 2. Information for incoming interrupt. Now one device can have more than one interrupt, and they are all handled by one workqueue structure. So we need to identify them. The previous patch enable gsi_msg_pending_bitmap get this done. 3. Mapping from host IRQ to guest gsi as well as guest gsi to real MSI/MSI-X message address/data. We used same entry number for the host and guest here, so that it's easy to find the correlated guest gsi. What we lack for now: 1. The PCI spec said nothing can existed with MSI-X table in the same page of MMIO region, except pending bits. The patch ignore pending bits as the first step (so they are always 0 - no pending). 2. The PCI spec allowed to change MSI-X table dynamically. That means, the OS can enable MSI-X, then mask one MSI-X entry, modify it, and unmask it. The patch didn't support this, and Linux also don't work in this way. 3. The patch didn't implement MSI-X mask all and mask single entry. I would implement the former in driver/pci/msi.c later. And for single entry, userspace should have reposibility to handle it. Signed-off-by: Sheng Yang <sheng@linux.intel.com> Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'virt/kvm/kvm_main.c')
-rw-r--r--virt/kvm/kvm_main.c98
1 files changed, 92 insertions, 6 deletions
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 8bd44d6985c7..3bed82754a5d 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -236,13 +236,33 @@ static void kvm_free_assigned_irq(struct kvm *kvm,
236 * now, the kvm state is still legal for probably we also have to wait 236 * now, the kvm state is still legal for probably we also have to wait
237 * interrupt_work done. 237 * interrupt_work done.
238 */ 238 */
239 disable_irq_nosync(assigned_dev->host_irq); 239 if (assigned_dev->irq_requested_type & KVM_ASSIGNED_DEV_MSIX) {
240 cancel_work_sync(&assigned_dev->interrupt_work); 240 int i;
241 for (i = 0; i < assigned_dev->entries_nr; i++)
242 disable_irq_nosync(assigned_dev->
243 host_msix_entries[i].vector);
244
245 cancel_work_sync(&assigned_dev->interrupt_work);
241 246
242 free_irq(assigned_dev->host_irq, (void *)assigned_dev); 247 for (i = 0; i < assigned_dev->entries_nr; i++)
248 free_irq(assigned_dev->host_msix_entries[i].vector,
249 (void *)assigned_dev);
243 250
244 if (assigned_dev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) 251 assigned_dev->entries_nr = 0;
245 pci_disable_msi(assigned_dev->dev); 252 kfree(assigned_dev->host_msix_entries);
253 kfree(assigned_dev->guest_msix_entries);
254 pci_disable_msix(assigned_dev->dev);
255 } else {
256 /* Deal with MSI and INTx */
257 disable_irq_nosync(assigned_dev->host_irq);
258 cancel_work_sync(&assigned_dev->interrupt_work);
259
260 free_irq(assigned_dev->host_irq, (void *)assigned_dev);
261
262 if (assigned_dev->irq_requested_type &
263 KVM_ASSIGNED_DEV_HOST_MSI)
264 pci_disable_msi(assigned_dev->dev);
265 }
246 266
247 assigned_dev->irq_requested_type = 0; 267 assigned_dev->irq_requested_type = 0;
248} 268}
@@ -373,6 +393,60 @@ static int assigned_device_update_msi(struct kvm *kvm,
373} 393}
374#endif 394#endif
375 395
396#ifdef __KVM_HAVE_MSIX
397static int assigned_device_update_msix(struct kvm *kvm,
398 struct kvm_assigned_dev_kernel *adev,
399 struct kvm_assigned_irq *airq)
400{
401 /* TODO Deal with KVM_DEV_IRQ_ASSIGNED_MASK_MSIX */
402 int i, r;
403
404 adev->ack_notifier.gsi = -1;
405
406 if (irqchip_in_kernel(kvm)) {
407 if (airq->flags & KVM_DEV_IRQ_ASSIGN_MASK_MSIX)
408 return -ENOTTY;
409
410 if (!(airq->flags & KVM_DEV_IRQ_ASSIGN_ENABLE_MSIX)) {
411 /* Guest disable MSI-X */
412 kvm_free_assigned_irq(kvm, adev);
413 if (msi2intx) {
414 pci_enable_msi(adev->dev);
415 if (adev->dev->msi_enabled)
416 return assigned_device_update_msi(kvm,
417 adev, airq);
418 }
419 return assigned_device_update_intx(kvm, adev, airq);
420 }
421
422 /* host_msix_entries and guest_msix_entries should have been
423 * initialized */
424 if (adev->entries_nr == 0)
425 return -EINVAL;
426
427 kvm_free_assigned_irq(kvm, adev);
428
429 r = pci_enable_msix(adev->dev, adev->host_msix_entries,
430 adev->entries_nr);
431 if (r)
432 return r;
433
434 for (i = 0; i < adev->entries_nr; i++) {
435 r = request_irq((adev->host_msix_entries + i)->vector,
436 kvm_assigned_dev_intr, 0,
437 "kvm_assigned_msix_device",
438 (void *)adev);
439 if (r)
440 return r;
441 }
442 }
443
444 adev->irq_requested_type |= KVM_ASSIGNED_DEV_MSIX;
445
446 return 0;
447}
448#endif
449
376static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, 450static int kvm_vm_ioctl_assign_irq(struct kvm *kvm,
377 struct kvm_assigned_irq 451 struct kvm_assigned_irq
378 *assigned_irq) 452 *assigned_irq)
@@ -417,12 +491,24 @@ static int kvm_vm_ioctl_assign_irq(struct kvm *kvm,
417 } 491 }
418 } 492 }
419 493
420 if ((match->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) && 494 if (match->irq_requested_type & KVM_ASSIGNED_DEV_MSIX)
495 current_flags |= KVM_DEV_IRQ_ASSIGN_ENABLE_MSIX;
496 else if ((match->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) &&
421 (match->irq_requested_type & KVM_ASSIGNED_DEV_GUEST_MSI)) 497 (match->irq_requested_type & KVM_ASSIGNED_DEV_GUEST_MSI))
422 current_flags |= KVM_DEV_IRQ_ASSIGN_ENABLE_MSI; 498 current_flags |= KVM_DEV_IRQ_ASSIGN_ENABLE_MSI;
423 499
424 changed_flags = assigned_irq->flags ^ current_flags; 500 changed_flags = assigned_irq->flags ^ current_flags;
425 501
502#ifdef __KVM_HAVE_MSIX
503 if (changed_flags & KVM_DEV_IRQ_ASSIGN_MSIX_ACTION) {
504 r = assigned_device_update_msix(kvm, match, assigned_irq);
505 if (r) {
506 printk(KERN_WARNING "kvm: failed to execute "
507 "MSI-X action!\n");
508 goto out_release;
509 }
510 } else
511#endif
426 if ((changed_flags & KVM_DEV_IRQ_ASSIGN_MSI_ACTION) || 512 if ((changed_flags & KVM_DEV_IRQ_ASSIGN_MSI_ACTION) ||
427 (msi2intx && match->dev->msi_enabled)) { 513 (msi2intx && match->dev->msi_enabled)) {
428#ifdef CONFIG_X86 514#ifdef CONFIG_X86