diff options
-rw-r--r-- | arch/x86/include/asm/kvm.h | 1 | ||||
-rw-r--r-- | include/linux/kvm.h | 8 | ||||
-rw-r--r-- | virt/kvm/kvm_main.c | 98 |
3 files changed, 101 insertions, 6 deletions
diff --git a/arch/x86/include/asm/kvm.h b/arch/x86/include/asm/kvm.h index dc3f6cf11704..125be8b19568 100644 --- a/arch/x86/include/asm/kvm.h +++ b/arch/x86/include/asm/kvm.h | |||
@@ -16,6 +16,7 @@ | |||
16 | #define __KVM_HAVE_MSI | 16 | #define __KVM_HAVE_MSI |
17 | #define __KVM_HAVE_USER_NMI | 17 | #define __KVM_HAVE_USER_NMI |
18 | #define __KVM_HAVE_GUEST_DEBUG | 18 | #define __KVM_HAVE_GUEST_DEBUG |
19 | #define __KVM_HAVE_MSIX | ||
19 | 20 | ||
20 | /* Architectural interrupt line count. */ | 21 | /* Architectural interrupt line count. */ |
21 | #define KVM_NR_INTERRUPTS 256 | 22 | #define KVM_NR_INTERRUPTS 256 |
diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 78cdee8c6355..640835ed2708 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h | |||
@@ -409,6 +409,9 @@ struct kvm_trace_rec { | |||
409 | #ifdef __KVM_HAVE_DEVICE_ASSIGNMENT | 409 | #ifdef __KVM_HAVE_DEVICE_ASSIGNMENT |
410 | #define KVM_CAP_DEVICE_DEASSIGNMENT 27 | 410 | #define KVM_CAP_DEVICE_DEASSIGNMENT 27 |
411 | #endif | 411 | #endif |
412 | #ifdef __KVM_HAVE_MSIX | ||
413 | #define KVM_CAP_DEVICE_MSIX 28 | ||
414 | #endif | ||
412 | /* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ | 415 | /* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ |
413 | #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 | 416 | #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 |
414 | 417 | ||
@@ -611,6 +614,11 @@ struct kvm_assigned_irq { | |||
611 | #define KVM_DEV_IRQ_ASSIGN_MSI_ACTION KVM_DEV_IRQ_ASSIGN_ENABLE_MSI | 614 | #define KVM_DEV_IRQ_ASSIGN_MSI_ACTION KVM_DEV_IRQ_ASSIGN_ENABLE_MSI |
612 | #define KVM_DEV_IRQ_ASSIGN_ENABLE_MSI (1 << 0) | 615 | #define KVM_DEV_IRQ_ASSIGN_ENABLE_MSI (1 << 0) |
613 | 616 | ||
617 | #define KVM_DEV_IRQ_ASSIGN_MSIX_ACTION (KVM_DEV_IRQ_ASSIGN_ENABLE_MSIX |\ | ||
618 | KVM_DEV_IRQ_ASSIGN_MASK_MSIX) | ||
619 | #define KVM_DEV_IRQ_ASSIGN_ENABLE_MSIX (1 << 1) | ||
620 | #define KVM_DEV_IRQ_ASSIGN_MASK_MSIX (1 << 2) | ||
621 | |||
614 | struct kvm_assigned_msix_nr { | 622 | struct kvm_assigned_msix_nr { |
615 | __u32 assigned_dev_id; | 623 | __u32 assigned_dev_id; |
616 | __u16 entry_nr; | 624 | __u16 entry_nr; |
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 | ||
397 | static 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 | |||
376 | static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, | 450 | static 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 |