diff options
Diffstat (limited to 'virt/kvm/assigned-dev.c')
-rw-r--r-- | virt/kvm/assigned-dev.c | 125 |
1 files changed, 46 insertions, 79 deletions
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c index 7c98928b09d9..ae72ae604c89 100644 --- a/virt/kvm/assigned-dev.c +++ b/virt/kvm/assigned-dev.c | |||
@@ -55,58 +55,31 @@ static int find_index_from_host_irq(struct kvm_assigned_dev_kernel | |||
55 | return index; | 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; | 60 | struct kvm_assigned_dev_kernel *assigned_dev = dev_id; |
61 | int i; | 61 | u32 vector; |
62 | int index; | ||
62 | 63 | ||
63 | assigned_dev = container_of(work, struct kvm_assigned_dev_kernel, | 64 | if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_INTX) { |
64 | interrupt_work); | 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 | if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) { | 71 | if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) { |
68 | struct kvm_guest_msix_entry *guest_entries = | 72 | index = find_index_from_host_irq(assigned_dev, irq); |
69 | assigned_dev->guest_msix_entries; | 73 | if (index >= 0) { |
70 | for (i = 0; i < assigned_dev->entries_nr; i++) { | 74 | vector = assigned_dev-> |
71 | if (!(guest_entries[i].flags & | 75 | guest_msix_entries[index].vector; |
72 | KVM_ASSIGNED_MSIX_PENDING)) | ||
73 | continue; | ||
74 | guest_entries[i].flags &= ~KVM_ASSIGNED_MSIX_PENDING; | ||
75 | kvm_set_irq(assigned_dev->kvm, | 76 | kvm_set_irq(assigned_dev->kvm, |
76 | assigned_dev->irq_source_id, | 77 | assigned_dev->irq_source_id, vector, 1); |
77 | guest_entries[i].vector, 1); | ||
78 | } | 78 | } |
79 | } else | 79 | } else |
80 | kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id, | 80 | kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id, |
81 | assigned_dev->guest_irq, 1); | 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 | return IRQ_HANDLED; | 83 | return IRQ_HANDLED; |
111 | } | 84 | } |
112 | 85 | ||
@@ -114,7 +87,6 @@ out: | |||
114 | static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian) | 87 | static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian) |
115 | { | 88 | { |
116 | struct kvm_assigned_dev_kernel *dev; | 89 | struct kvm_assigned_dev_kernel *dev; |
117 | unsigned long flags; | ||
118 | 90 | ||
119 | if (kian->gsi == -1) | 91 | if (kian->gsi == -1) |
120 | return; | 92 | return; |
@@ -127,12 +99,12 @@ static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian) | |||
127 | /* The guest irq may be shared so this ack may be | 99 | /* The guest irq may be shared so this ack may be |
128 | * from another device. | 100 | * from another device. |
129 | */ | 101 | */ |
130 | spin_lock_irqsave(&dev->assigned_dev_lock, flags); | 102 | spin_lock(&dev->intx_lock); |
131 | if (dev->host_irq_disabled) { | 103 | if (dev->host_irq_disabled) { |
132 | enable_irq(dev->host_irq); | 104 | enable_irq(dev->host_irq); |
133 | dev->host_irq_disabled = false; | 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 | static void deassign_guest_irq(struct kvm *kvm, | 110 | static void deassign_guest_irq(struct kvm *kvm, |
@@ -141,6 +113,9 @@ static void deassign_guest_irq(struct kvm *kvm, | |||
141 | kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier); | 113 | kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier); |
142 | assigned_dev->ack_notifier.gsi = -1; | 114 | assigned_dev->ack_notifier.gsi = -1; |
143 | 115 | ||
116 | kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id, | ||
117 | assigned_dev->guest_irq, 0); | ||
118 | |||
144 | if (assigned_dev->irq_source_id != -1) | 119 | if (assigned_dev->irq_source_id != -1) |
145 | kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id); | 120 | kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id); |
146 | assigned_dev->irq_source_id = -1; | 121 | assigned_dev->irq_source_id = -1; |
@@ -152,28 +127,19 @@ static void deassign_host_irq(struct kvm *kvm, | |||
152 | struct kvm_assigned_dev_kernel *assigned_dev) | 127 | struct kvm_assigned_dev_kernel *assigned_dev) |
153 | { | 128 | { |
154 | /* | 129 | /* |
155 | * In kvm_free_device_irq, cancel_work_sync return true if: | 130 | * We disable irq here to prevent further events. |
156 | * 1. work is scheduled, and then cancelled. | ||
157 | * 2. work callback is executed. | ||
158 | * | ||
159 | * The first one ensured that the irq is disabled and no more events | ||
160 | * would happen. But for the second one, the irq may be enabled (e.g. | ||
161 | * for MSI). So we disable irq here to prevent further events. | ||
162 | * | 131 | * |
163 | * Notice this maybe result in nested disable if the interrupt type is | 132 | * Notice this maybe result in nested disable if the interrupt type is |
164 | * INTx, but it's OK for we are going to free it. | 133 | * INTx, but it's OK for we are going to free it. |
165 | * | 134 | * |
166 | * If this function is a part of VM destroy, please ensure that till | 135 | * If this function is a part of VM destroy, please ensure that till |
167 | * now, the kvm state is still legal for probably we also have to wait | 136 | * now, the kvm state is still legal for probably we also have to wait |
168 | * interrupt_work done. | 137 | * on a currently running IRQ handler. |
169 | */ | 138 | */ |
170 | if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) { | 139 | if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) { |
171 | int i; | 140 | int i; |
172 | for (i = 0; i < assigned_dev->entries_nr; i++) | 141 | for (i = 0; i < assigned_dev->entries_nr; i++) |
173 | disable_irq_nosync(assigned_dev-> | 142 | disable_irq(assigned_dev->host_msix_entries[i].vector); |
174 | host_msix_entries[i].vector); | ||
175 | |||
176 | cancel_work_sync(&assigned_dev->interrupt_work); | ||
177 | 143 | ||
178 | for (i = 0; i < assigned_dev->entries_nr; i++) | 144 | for (i = 0; i < assigned_dev->entries_nr; i++) |
179 | free_irq(assigned_dev->host_msix_entries[i].vector, | 145 | free_irq(assigned_dev->host_msix_entries[i].vector, |
@@ -185,8 +151,7 @@ static void deassign_host_irq(struct kvm *kvm, | |||
185 | pci_disable_msix(assigned_dev->dev); | 151 | pci_disable_msix(assigned_dev->dev); |
186 | } else { | 152 | } else { |
187 | /* Deal with MSI and INTx */ | 153 | /* Deal with MSI and INTx */ |
188 | disable_irq_nosync(assigned_dev->host_irq); | 154 | disable_irq(assigned_dev->host_irq); |
189 | cancel_work_sync(&assigned_dev->interrupt_work); | ||
190 | 155 | ||
191 | free_irq(assigned_dev->host_irq, (void *)assigned_dev); | 156 | free_irq(assigned_dev->host_irq, (void *)assigned_dev); |
192 | 157 | ||
@@ -232,7 +197,8 @@ static void kvm_free_assigned_device(struct kvm *kvm, | |||
232 | { | 197 | { |
233 | kvm_free_assigned_irq(kvm, assigned_dev); | 198 | kvm_free_assigned_irq(kvm, assigned_dev); |
234 | 199 | ||
235 | pci_reset_function(assigned_dev->dev); | 200 | __pci_reset_function(assigned_dev->dev); |
201 | pci_restore_state(assigned_dev->dev); | ||
236 | 202 | ||
237 | pci_release_regions(assigned_dev->dev); | 203 | pci_release_regions(assigned_dev->dev); |
238 | pci_disable_device(assigned_dev->dev); | 204 | pci_disable_device(assigned_dev->dev); |
@@ -265,8 +231,8 @@ static int assigned_device_enable_host_intx(struct kvm *kvm, | |||
265 | * on the same interrupt line is not a happy situation: there | 231 | * on the same interrupt line is not a happy situation: there |
266 | * are going to be long delays in accepting, acking, etc. | 232 | * are going to be long delays in accepting, acking, etc. |
267 | */ | 233 | */ |
268 | if (request_irq(dev->host_irq, kvm_assigned_dev_intr, | 234 | if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread, |
269 | 0, "kvm_assigned_intx_device", (void *)dev)) | 235 | IRQF_ONESHOT, dev->irq_name, (void *)dev)) |
270 | return -EIO; | 236 | return -EIO; |
271 | return 0; | 237 | return 0; |
272 | } | 238 | } |
@@ -284,8 +250,8 @@ static int assigned_device_enable_host_msi(struct kvm *kvm, | |||
284 | } | 250 | } |
285 | 251 | ||
286 | dev->host_irq = dev->dev->irq; | 252 | dev->host_irq = dev->dev->irq; |
287 | if (request_irq(dev->host_irq, kvm_assigned_dev_intr, 0, | 253 | if (request_threaded_irq(dev->host_irq, NULL, kvm_assigned_dev_thread, |
288 | "kvm_assigned_msi_device", (void *)dev)) { | 254 | 0, dev->irq_name, (void *)dev)) { |
289 | pci_disable_msi(dev->dev); | 255 | pci_disable_msi(dev->dev); |
290 | return -EIO; | 256 | return -EIO; |
291 | } | 257 | } |
@@ -310,10 +276,9 @@ static int assigned_device_enable_host_msix(struct kvm *kvm, | |||
310 | return r; | 276 | return r; |
311 | 277 | ||
312 | for (i = 0; i < dev->entries_nr; i++) { | 278 | for (i = 0; i < dev->entries_nr; i++) { |
313 | r = request_irq(dev->host_msix_entries[i].vector, | 279 | r = request_threaded_irq(dev->host_msix_entries[i].vector, |
314 | kvm_assigned_dev_intr, 0, | 280 | NULL, kvm_assigned_dev_thread, |
315 | "kvm_assigned_msix_device", | 281 | 0, dev->irq_name, (void *)dev); |
316 | (void *)dev); | ||
317 | if (r) | 282 | if (r) |
318 | goto err; | 283 | goto err; |
319 | } | 284 | } |
@@ -370,6 +335,9 @@ static int assign_host_irq(struct kvm *kvm, | |||
370 | if (dev->irq_requested_type & KVM_DEV_IRQ_HOST_MASK) | 335 | if (dev->irq_requested_type & KVM_DEV_IRQ_HOST_MASK) |
371 | return r; | 336 | return r; |
372 | 337 | ||
338 | snprintf(dev->irq_name, sizeof(dev->irq_name), "kvm:%s", | ||
339 | pci_name(dev->dev)); | ||
340 | |||
373 | switch (host_irq_type) { | 341 | switch (host_irq_type) { |
374 | case KVM_DEV_IRQ_HOST_INTX: | 342 | case KVM_DEV_IRQ_HOST_INTX: |
375 | r = assigned_device_enable_host_intx(kvm, dev); | 343 | r = assigned_device_enable_host_intx(kvm, dev); |
@@ -547,6 +515,7 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | |||
547 | } | 515 | } |
548 | 516 | ||
549 | pci_reset_function(dev); | 517 | pci_reset_function(dev); |
518 | pci_save_state(dev); | ||
550 | 519 | ||
551 | match->assigned_dev_id = assigned_dev->assigned_dev_id; | 520 | match->assigned_dev_id = assigned_dev->assigned_dev_id; |
552 | match->host_segnr = assigned_dev->segnr; | 521 | match->host_segnr = assigned_dev->segnr; |
@@ -554,12 +523,10 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | |||
554 | match->host_devfn = assigned_dev->devfn; | 523 | match->host_devfn = assigned_dev->devfn; |
555 | match->flags = assigned_dev->flags; | 524 | match->flags = assigned_dev->flags; |
556 | match->dev = dev; | 525 | match->dev = dev; |
557 | spin_lock_init(&match->assigned_dev_lock); | 526 | spin_lock_init(&match->intx_lock); |
558 | match->irq_source_id = -1; | 527 | match->irq_source_id = -1; |
559 | match->kvm = kvm; | 528 | match->kvm = kvm; |
560 | match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq; | 529 | match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq; |
561 | INIT_WORK(&match->interrupt_work, | ||
562 | kvm_assigned_dev_interrupt_work_handler); | ||
563 | 530 | ||
564 | list_add(&match->list, &kvm->arch.assigned_dev_head); | 531 | list_add(&match->list, &kvm->arch.assigned_dev_head); |
565 | 532 | ||
@@ -579,6 +546,7 @@ out: | |||
579 | mutex_unlock(&kvm->lock); | 546 | mutex_unlock(&kvm->lock); |
580 | return r; | 547 | return r; |
581 | out_list_del: | 548 | out_list_del: |
549 | pci_restore_state(dev); | ||
582 | list_del(&match->list); | 550 | list_del(&match->list); |
583 | pci_release_regions(dev); | 551 | pci_release_regions(dev); |
584 | out_disable: | 552 | out_disable: |
@@ -651,9 +619,9 @@ static int kvm_vm_ioctl_set_msix_nr(struct kvm *kvm, | |||
651 | r = -ENOMEM; | 619 | r = -ENOMEM; |
652 | goto msix_nr_out; | 620 | goto msix_nr_out; |
653 | } | 621 | } |
654 | adev->guest_msix_entries = kzalloc( | 622 | adev->guest_msix_entries = |
655 | sizeof(struct kvm_guest_msix_entry) * | 623 | kzalloc(sizeof(struct msix_entry) * entry_nr->entry_nr, |
656 | entry_nr->entry_nr, GFP_KERNEL); | 624 | GFP_KERNEL); |
657 | if (!adev->guest_msix_entries) { | 625 | if (!adev->guest_msix_entries) { |
658 | kfree(adev->host_msix_entries); | 626 | kfree(adev->host_msix_entries); |
659 | r = -ENOMEM; | 627 | r = -ENOMEM; |
@@ -706,7 +674,7 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, | |||
706 | unsigned long arg) | 674 | unsigned long arg) |
707 | { | 675 | { |
708 | void __user *argp = (void __user *)arg; | 676 | void __user *argp = (void __user *)arg; |
709 | int r = -ENOTTY; | 677 | int r; |
710 | 678 | ||
711 | switch (ioctl) { | 679 | switch (ioctl) { |
712 | case KVM_ASSIGN_PCI_DEVICE: { | 680 | case KVM_ASSIGN_PCI_DEVICE: { |
@@ -724,7 +692,6 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, | |||
724 | r = -EOPNOTSUPP; | 692 | r = -EOPNOTSUPP; |
725 | break; | 693 | break; |
726 | } | 694 | } |
727 | #ifdef KVM_CAP_ASSIGN_DEV_IRQ | ||
728 | case KVM_ASSIGN_DEV_IRQ: { | 695 | case KVM_ASSIGN_DEV_IRQ: { |
729 | struct kvm_assigned_irq assigned_irq; | 696 | struct kvm_assigned_irq assigned_irq; |
730 | 697 | ||
@@ -747,8 +714,6 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, | |||
747 | goto out; | 714 | goto out; |
748 | break; | 715 | break; |
749 | } | 716 | } |
750 | #endif | ||
751 | #ifdef KVM_CAP_DEVICE_DEASSIGNMENT | ||
752 | case KVM_DEASSIGN_PCI_DEVICE: { | 717 | case KVM_DEASSIGN_PCI_DEVICE: { |
753 | struct kvm_assigned_pci_dev assigned_dev; | 718 | struct kvm_assigned_pci_dev assigned_dev; |
754 | 719 | ||
@@ -760,7 +725,6 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, | |||
760 | goto out; | 725 | goto out; |
761 | break; | 726 | break; |
762 | } | 727 | } |
763 | #endif | ||
764 | #ifdef KVM_CAP_IRQ_ROUTING | 728 | #ifdef KVM_CAP_IRQ_ROUTING |
765 | case KVM_SET_GSI_ROUTING: { | 729 | case KVM_SET_GSI_ROUTING: { |
766 | struct kvm_irq_routing routing; | 730 | struct kvm_irq_routing routing; |
@@ -813,6 +777,9 @@ long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, | |||
813 | break; | 777 | break; |
814 | } | 778 | } |
815 | #endif | 779 | #endif |
780 | default: | ||
781 | r = -ENOTTY; | ||
782 | break; | ||
816 | } | 783 | } |
817 | out: | 784 | out: |
818 | return r; | 785 | return r; |