diff options
-rw-r--r-- | include/linux/kvm.h | 3 | ||||
-rw-r--r-- | virt/kvm/kvm_main.c | 81 |
2 files changed, 78 insertions, 6 deletions
diff --git a/include/linux/kvm.h b/include/linux/kvm.h index bb283c388a24..0997e6f5490c 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h | |||
@@ -392,6 +392,9 @@ struct kvm_trace_rec { | |||
392 | #endif | 392 | #endif |
393 | #define KVM_CAP_IOMMU 18 | 393 | #define KVM_CAP_IOMMU 18 |
394 | #define KVM_CAP_NMI 19 | 394 | #define KVM_CAP_NMI 19 |
395 | #if defined(CONFIG_X86) | ||
396 | #define KVM_CAP_DEVICE_MSI 20 | ||
397 | #endif | ||
395 | 398 | ||
396 | /* | 399 | /* |
397 | * ioctls for VM fds | 400 | * ioctls for VM fds |
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 228c1d18a614..bf36ae9ae7df 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -159,9 +159,15 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work) | |||
159 | * finer-grained lock, update this | 159 | * finer-grained lock, update this |
160 | */ | 160 | */ |
161 | mutex_lock(&assigned_dev->kvm->lock); | 161 | mutex_lock(&assigned_dev->kvm->lock); |
162 | kvm_set_irq(assigned_dev->kvm, | 162 | if (assigned_dev->irq_requested_type & KVM_ASSIGNED_DEV_GUEST_INTX) |
163 | assigned_dev->irq_source_id, | 163 | kvm_set_irq(assigned_dev->kvm, |
164 | assigned_dev->guest_irq, 1); | 164 | assigned_dev->irq_source_id, |
165 | assigned_dev->guest_irq, 1); | ||
166 | else if (assigned_dev->irq_requested_type & | ||
167 | KVM_ASSIGNED_DEV_GUEST_MSI) { | ||
168 | assigned_device_msi_dispatch(assigned_dev); | ||
169 | enable_irq(assigned_dev->host_irq); | ||
170 | } | ||
165 | mutex_unlock(&assigned_dev->kvm->lock); | 171 | mutex_unlock(&assigned_dev->kvm->lock); |
166 | kvm_put_kvm(assigned_dev->kvm); | 172 | kvm_put_kvm(assigned_dev->kvm); |
167 | } | 173 | } |
@@ -197,6 +203,8 @@ static void kvm_free_assigned_device(struct kvm *kvm, | |||
197 | { | 203 | { |
198 | if (irqchip_in_kernel(kvm) && assigned_dev->irq_requested_type) | 204 | if (irqchip_in_kernel(kvm) && assigned_dev->irq_requested_type) |
199 | free_irq(assigned_dev->host_irq, (void *)assigned_dev); | 205 | free_irq(assigned_dev->host_irq, (void *)assigned_dev); |
206 | if (assigned_dev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) | ||
207 | pci_disable_msi(assigned_dev->dev); | ||
200 | 208 | ||
201 | kvm_unregister_irq_ack_notifier(&assigned_dev->ack_notifier); | 209 | kvm_unregister_irq_ack_notifier(&assigned_dev->ack_notifier); |
202 | kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id); | 210 | kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id); |
@@ -242,6 +250,11 @@ static int assigned_device_update_intx(struct kvm *kvm, | |||
242 | return 0; | 250 | return 0; |
243 | 251 | ||
244 | if (irqchip_in_kernel(kvm)) { | 252 | if (irqchip_in_kernel(kvm)) { |
253 | if (adev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) { | ||
254 | free_irq(adev->host_irq, (void *)kvm); | ||
255 | pci_disable_msi(adev->dev); | ||
256 | } | ||
257 | |||
245 | if (!capable(CAP_SYS_RAWIO)) | 258 | if (!capable(CAP_SYS_RAWIO)) |
246 | return -EPERM; | 259 | return -EPERM; |
247 | 260 | ||
@@ -265,6 +278,41 @@ static int assigned_device_update_intx(struct kvm *kvm, | |||
265 | return 0; | 278 | return 0; |
266 | } | 279 | } |
267 | 280 | ||
281 | #ifdef CONFIG_X86 | ||
282 | static int assigned_device_update_msi(struct kvm *kvm, | ||
283 | struct kvm_assigned_dev_kernel *adev, | ||
284 | struct kvm_assigned_irq *airq) | ||
285 | { | ||
286 | int r; | ||
287 | |||
288 | /* x86 don't care upper address of guest msi message addr */ | ||
289 | adev->guest_msi.address_lo = airq->guest_msi.addr_lo; | ||
290 | adev->guest_msi.data = airq->guest_msi.data; | ||
291 | adev->ack_notifier.gsi = -1; | ||
292 | |||
293 | if (adev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_MSI) | ||
294 | return 0; | ||
295 | |||
296 | if (irqchip_in_kernel(kvm)) { | ||
297 | if (adev->irq_requested_type & KVM_ASSIGNED_DEV_HOST_INTX) | ||
298 | free_irq(adev->host_irq, (void *)adev); | ||
299 | |||
300 | r = pci_enable_msi(adev->dev); | ||
301 | if (r) | ||
302 | return r; | ||
303 | |||
304 | adev->host_irq = adev->dev->irq; | ||
305 | if (request_irq(adev->host_irq, kvm_assigned_dev_intr, 0, | ||
306 | "kvm_assigned_msi_device", (void *)adev)) | ||
307 | return -EIO; | ||
308 | } | ||
309 | |||
310 | adev->irq_requested_type = KVM_ASSIGNED_DEV_GUEST_MSI | | ||
311 | KVM_ASSIGNED_DEV_HOST_MSI; | ||
312 | return 0; | ||
313 | } | ||
314 | #endif | ||
315 | |||
268 | static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, | 316 | static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, |
269 | struct kvm_assigned_irq | 317 | struct kvm_assigned_irq |
270 | *assigned_irq) | 318 | *assigned_irq) |
@@ -301,9 +349,30 @@ static int kvm_vm_ioctl_assign_irq(struct kvm *kvm, | |||
301 | } | 349 | } |
302 | } | 350 | } |
303 | 351 | ||
304 | r = assigned_device_update_intx(kvm, match, assigned_irq); | 352 | if (assigned_irq->flags & KVM_DEV_IRQ_ASSIGN_ENABLE_MSI) { |
305 | if (r) | 353 | #ifdef CONFIG_X86 |
306 | goto out_release; | 354 | r = assigned_device_update_msi(kvm, match, assigned_irq); |
355 | if (r) { | ||
356 | printk(KERN_WARNING "kvm: failed to enable " | ||
357 | "MSI device!\n"); | ||
358 | goto out_release; | ||
359 | } | ||
360 | #else | ||
361 | r = -ENOTTY; | ||
362 | #endif | ||
363 | } else if (assigned_irq->host_irq == 0 && match->dev->irq == 0) { | ||
364 | /* Host device IRQ 0 means don't support INTx */ | ||
365 | printk(KERN_WARNING "kvm: wait device to enable MSI!\n"); | ||
366 | r = 0; | ||
367 | } else { | ||
368 | /* Non-sharing INTx mode */ | ||
369 | r = assigned_device_update_intx(kvm, match, assigned_irq); | ||
370 | if (r) { | ||
371 | printk(KERN_WARNING "kvm: failed to enable " | ||
372 | "INTx device!\n"); | ||
373 | goto out_release; | ||
374 | } | ||
375 | } | ||
307 | 376 | ||
308 | mutex_unlock(&kvm->lock); | 377 | mutex_unlock(&kvm->lock); |
309 | return r; | 378 | return r; |