ivshmem: use kvm irqfd for msi notifications
Use irqfd for improving context switch when notifying the guest. If the host doesn't support kvm irqfd, regular msi notifications are still supported. Note: the ivshmem implementation doesn't allow switching between MSI and IO interrupts, this patch doesn't either. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
		
							parent
							
								
									0f57350e5c
								
							
						
					
					
						commit
						660c97eef6
					
				| @ -19,6 +19,7 @@ | ||||
| #include "hw/hw.h" | ||||
| #include "hw/i386/pc.h" | ||||
| #include "hw/pci/pci.h" | ||||
| #include "hw/pci/msi.h" | ||||
| #include "hw/pci/msix.h" | ||||
| #include "sysemu/kvm.h" | ||||
| #include "migration/migration.h" | ||||
| @ -68,6 +69,7 @@ typedef struct Peer { | ||||
| 
 | ||||
| typedef struct MSIVector { | ||||
|     PCIDevice *pdev; | ||||
|     int virq; | ||||
| } MSIVector; | ||||
| 
 | ||||
| typedef struct IVShmemState { | ||||
| @ -293,13 +295,73 @@ static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { | ||||
|     msix_notify(pdev, vector); | ||||
| } | ||||
| 
 | ||||
| static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, | ||||
|                                  MSIMessage msg) | ||||
| { | ||||
|     IVShmemState *s = IVSHMEM(dev); | ||||
|     EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; | ||||
|     MSIVector *v = &s->msi_vectors[vector]; | ||||
|     int ret; | ||||
| 
 | ||||
|     IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector); | ||||
| 
 | ||||
|     ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); | ||||
| } | ||||
| 
 | ||||
| static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) | ||||
| { | ||||
|     IVShmemState *s = IVSHMEM(dev); | ||||
|     EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; | ||||
|     int ret; | ||||
| 
 | ||||
|     IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector); | ||||
| 
 | ||||
|     ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, | ||||
|                                                 s->msi_vectors[vector].virq); | ||||
|     if (ret != 0) { | ||||
|         error_report("remove_irqfd_notifier_gsi failed"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void ivshmem_vector_poll(PCIDevice *dev, | ||||
|                                 unsigned int vector_start, | ||||
|                                 unsigned int vector_end) | ||||
| { | ||||
|     IVShmemState *s = IVSHMEM(dev); | ||||
|     unsigned int vector; | ||||
| 
 | ||||
|     IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end); | ||||
| 
 | ||||
|     vector_end = MIN(vector_end, s->vectors); | ||||
| 
 | ||||
|     for (vector = vector_start; vector < vector_end; vector++) { | ||||
|         EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector]; | ||||
| 
 | ||||
|         if (!msix_is_masked(dev, vector)) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if (event_notifier_test_and_clear(notifier)) { | ||||
|             msix_set_pending(dev, vector); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n, | ||||
|                                                   int vector) | ||||
| { | ||||
|     /* create a event character device based on the passed eventfd */ | ||||
|     IVShmemState *s = opaque; | ||||
|     CharDriverState * chr; | ||||
|     PCIDevice *pdev = PCI_DEVICE(s); | ||||
|     int eventfd = event_notifier_get_fd(n); | ||||
|     CharDriverState *chr; | ||||
| 
 | ||||
|     s->msi_vectors[vector].pdev = pdev; | ||||
| 
 | ||||
|     chr = qemu_chr_open_eventfd(eventfd); | ||||
| 
 | ||||
| @ -484,6 +546,58 @@ static bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size, | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static int ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector) | ||||
| { | ||||
|     PCIDevice *pdev = PCI_DEVICE(s); | ||||
|     MSIMessage msg = msix_get_message(pdev, vector); | ||||
|     int ret; | ||||
| 
 | ||||
|     IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); | ||||
| 
 | ||||
|     if (s->msi_vectors[vector].pdev != NULL) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev); | ||||
|     if (ret < 0) { | ||||
|         error_report("ivshmem: kvm_irqchip_add_msi_route failed"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     s->msi_vectors[vector].virq = ret; | ||||
|     s->msi_vectors[vector].pdev = pdev; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void setup_interrupt(IVShmemState *s, int vector) | ||||
| { | ||||
|     EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; | ||||
|     bool with_irqfd = kvm_msi_via_irqfd_enabled() && | ||||
|         ivshmem_has_feature(s, IVSHMEM_MSI); | ||||
|     PCIDevice *pdev = PCI_DEVICE(s); | ||||
| 
 | ||||
|     IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector); | ||||
| 
 | ||||
|     if (!with_irqfd) { | ||||
|         IVSHMEM_DPRINTF("with eventfd"); | ||||
|         s->eventfd_chr[vector] = create_eventfd_chr_device(s, n, vector); | ||||
|     } else if (msix_enabled(pdev)) { | ||||
|         IVSHMEM_DPRINTF("with irqfd"); | ||||
|         if (ivshmem_add_kvm_msi_virq(s, vector) < 0) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!msix_is_masked(pdev, vector)) { | ||||
|             kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, | ||||
|                                                s->msi_vectors[vector].virq); | ||||
|         } | ||||
|     } else { | ||||
|         /* it will be delayed until msix is enabled, in write_config */ | ||||
|         IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void ivshmem_read(void *opaque, const uint8_t *buf, int size) | ||||
| { | ||||
|     IVShmemState *s = opaque; | ||||
| @ -586,11 +700,10 @@ static void ivshmem_read(void *opaque, const uint8_t *buf, int size) | ||||
|     IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, | ||||
|                     new_eventfd, incoming_fd); | ||||
|     event_notifier_init_fd(&peer->eventfds[new_eventfd], incoming_fd); | ||||
|     fcntl_setfl(incoming_fd, O_NONBLOCK); /* msix/irqfd poll non block */ | ||||
| 
 | ||||
|     if (incoming_posn == s->vm_id) { | ||||
|         s->eventfd_chr[new_eventfd] = create_eventfd_chr_device(s, | ||||
|                    &s->peers[s->vm_id].eventfds[new_eventfd], | ||||
|                    new_eventfd); | ||||
|         setup_interrupt(s, new_eventfd); | ||||
|     } | ||||
| 
 | ||||
|     if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { | ||||
| @ -665,10 +778,65 @@ static int ivshmem_setup_msi(IVShmemState * s) | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address, | ||||
| static void ivshmem_enable_irqfd(IVShmemState *s) | ||||
| { | ||||
|     PCIDevice *pdev = PCI_DEVICE(s); | ||||
|     int i; | ||||
| 
 | ||||
|     for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { | ||||
|         ivshmem_add_kvm_msi_virq(s, i); | ||||
|     } | ||||
| 
 | ||||
|     if (msix_set_vector_notifiers(pdev, | ||||
|                                   ivshmem_vector_unmask, | ||||
|                                   ivshmem_vector_mask, | ||||
|                                   ivshmem_vector_poll)) { | ||||
|         error_report("ivshmem: msix_set_vector_notifiers failed"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector) | ||||
| { | ||||
|     IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); | ||||
| 
 | ||||
|     if (s->msi_vectors[vector].pdev == NULL) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     /* it was cleaned when masked in the frontend. */ | ||||
|     kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); | ||||
| 
 | ||||
|     s->msi_vectors[vector].pdev = NULL; | ||||
| } | ||||
| 
 | ||||
| static void ivshmem_disable_irqfd(IVShmemState *s) | ||||
| { | ||||
|     PCIDevice *pdev = PCI_DEVICE(s); | ||||
|     int i; | ||||
| 
 | ||||
|     for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { | ||||
|         ivshmem_remove_kvm_msi_virq(s, i); | ||||
|     } | ||||
| 
 | ||||
|     msix_unset_vector_notifiers(pdev); | ||||
| } | ||||
| 
 | ||||
| static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, | ||||
|                                  uint32_t val, int len) | ||||
| { | ||||
|     pci_default_write_config(pci_dev, address, val, len); | ||||
|     IVShmemState *s = IVSHMEM(pdev); | ||||
|     int is_enabled, was_enabled = msix_enabled(pdev); | ||||
| 
 | ||||
|     pci_default_write_config(pdev, address, val, len); | ||||
|     is_enabled = msix_enabled(pdev); | ||||
| 
 | ||||
|     if (kvm_msi_via_irqfd_enabled() && s->vm_id != -1) { | ||||
|         if (!was_enabled && is_enabled) { | ||||
|             ivshmem_enable_irqfd(s); | ||||
|         } else if (was_enabled && !is_enabled) { | ||||
|             ivshmem_disable_irqfd(s); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pci_ivshmem_realize(PCIDevice *dev, Error **errp) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Marc-André Lureau
						Marc-André Lureau