static void ivshmem_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); if (!qtest_enabled()) { error_report("ivshmem is deprecated, please use ivshmem-plain" " or ivshmem-doorbell instead"); } if (!!qemu_chr_fe_get_driver(&s->server_chr) + !!s->shmobj != 1) { error_setg(errp, "You must specify either 'shm' or 'chardev'"); return; } if (s->sizearg == NULL) { s->legacy_size = 4 << 20; /* 4 MB default */ } else { char *end; int64_t size = qemu_strtosz(s->sizearg, &end); if (size < 0 || (size_t)size != size || *end != '\0' || !is_power_of_2(size)) { error_setg(errp, "Invalid size %s", s->sizearg); return; } s->legacy_size = size; } /* check that role is reasonable */ if (s->role) { if (strncmp(s->role, "peer", 5) == 0) { s->master = ON_OFF_AUTO_OFF; } else if (strncmp(s->role, "master", 7) == 0) { s->master = ON_OFF_AUTO_ON; } else { error_setg(errp, "'role' must be 'peer' or 'master'"); return; } } else { s->master = ON_OFF_AUTO_AUTO; } if (s->shmobj) { desugar_shm(s); } /* * Note: we don't use INTx with IVSHMEM_MSI at all, so this is a * bald-faced lie then. But it's a backwards compatible lie. */ pci_config_set_interrupt_pin(dev->config, 1); ivshmem_common_realize(dev, errp); }
static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); if (!s->hostmem) { error_setg(errp, "You must specify a 'memdev'"); return; } ivshmem_common_realize(dev, errp); host_memory_backend_set_mapped(s->hostmem, true); }
static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) { IVShmemState *s = IVSHMEM_COMMON(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_write_config(PCIDevice *pdev, uint32_t address, uint32_t val, int len) { IVShmemState *s = IVSHMEM_COMMON(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()) { if (!was_enabled && is_enabled) { ivshmem_enable_irqfd(s); } else if (was_enabled && !is_enabled) { ivshmem_disable_irqfd(s); } } }
static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, MSIMessage msg) { IVShmemState *s = IVSHMEM_COMMON(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; } kvm_irqchip_commit_routes(kvm_state); return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); }
static void ivshmem_vector_notify(void *opaque) { MSIVector *entry = opaque; PCIDevice *pdev = entry->pdev; IVShmemState *s = IVSHMEM_COMMON(pdev); int vector = entry - s->msi_vectors; EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; if (!event_notifier_test_and_clear(n)) { return; } IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector); if (ivshmem_has_feature(s, IVSHMEM_MSI)) { if (msix_enabled(pdev)) { msix_notify(pdev, vector); } } else { ivshmem_IntrStatus_write(s, 1); } }
static void ivshmem_exit(PCIDevice *dev) { IVShmemState *s = IVSHMEM_COMMON(dev); int i; if (s->migration_blocker) { migrate_del_blocker(s->migration_blocker); error_free(s->migration_blocker); } if (memory_region_is_mapped(s->ivshmem_bar2)) { if (!s->hostmem) { void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2); int fd; if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) { error_report("Failed to munmap shared memory %s", strerror(errno)); } fd = memory_region_get_fd(s->ivshmem_bar2); close(fd); } vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); } if (s->peers) { for (i = 0; i < s->nb_peers; i++) { close_peer_eventfds(s, i); } g_free(s->peers); } if (ivshmem_has_feature(s, IVSHMEM_MSI)) { msix_uninit_exclusive_bar(dev); } g_free(s->msi_vectors); }
static void ivshmem_vector_poll(PCIDevice *dev, unsigned int vector_start, unsigned int vector_end) { IVShmemState *s = IVSHMEM_COMMON(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 void ivshmem_common_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); Error *err = NULL; uint8_t *pci_conf; uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_PREFETCH; /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && !ivshmem_has_feature(s, IVSHMEM_MSI)) { error_setg(errp, "ioeventfd/irqfd requires MSI"); return; } pci_conf = dev->config; pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); /* region for registers*/ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ivshmem_mmio); if (!s->not_legacy_32bit) { attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; } if (s->hostmem != NULL) { IVSHMEM_DPRINTF("using hostmem\n"); s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, &error_abort); } else { assert(s->server_chr); IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", s->server_chr->filename); /* we allocate enough space for 16 peers and grow as needed */ resize_peers(s, 16); /* * Receive setup messages from server synchronously. * Older versions did it asynchronously, but that creates a * number of entertaining race conditions. */ ivshmem_recv_setup(s, &err); if (err) { error_propagate(errp, err); return; } if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) { error_setg(errp, "master must connect to the server before any peers"); return; } qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, NULL, s); if (ivshmem_setup_interrupts(s) < 0) { error_setg(errp, "failed to initialize interrupts"); return; } } vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2); if (s->master == ON_OFF_AUTO_AUTO) { s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } if (!ivshmem_is_master(s)) { error_setg(&s->migration_blocker, "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); migrate_add_blocker(s->migration_blocker); } }
static void ivshmem_plain_exit(PCIDevice *pci_dev) { IVShmemState *s = IVSHMEM_COMMON(pci_dev); host_memory_backend_set_mapped(s->hostmem, false); }