static void ivshmem_reset(DeviceState *d) { IVShmemState *s = IVSHMEM(d); s->intrstatus = 0; ivshmem_use_msix(s); }
static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { MSIVector *entry = opaque; PCIDevice *pdev = entry->pdev; IVShmemState *s = IVSHMEM(pdev); int vector = entry - s->msi_vectors; IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector); msix_notify(pdev, vector); }
static void ivshmem_init(Object *obj) { IVShmemState *s = IVSHMEM(obj); object_property_add_link(obj, "x-memdev", TYPE_MEMORY_BACKEND, (Object **)&s->hostmem, ivshmem_check_memdev_is_busy, OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); }
static void pci_ivshmem_exit(PCIDevice *dev) { IVShmemState *s = IVSHMEM(dev); int i; fifo8_destroy(&s->incoming_fifo); if (s->migration_blocker) { migrate_del_blocker(s->migration_blocker); error_free(s->migration_blocker); } if (memory_region_is_mapped(&s->ivshmem)) { if (!s->hostmem) { void *addr = memory_region_get_ram_ptr(&s->ivshmem); int fd; if (munmap(addr, s->ivshmem_size) == -1) { error_report("Failed to munmap shared memory %s", strerror(errno)); } fd = qemu_get_ram_fd(memory_region_get_ram_addr(&s->ivshmem)); if (fd != -1) { close(fd); } } vmstate_unregister_ram(&s->ivshmem, DEVICE(dev)); memory_region_del_subregion(&s->bar, &s->ivshmem); } if (s->eventfd_chr) { for (i = 0; i < s->vectors; i++) { if (s->eventfd_chr[i]) { qemu_chr_free(s->eventfd_chr[i]); } } g_free(s->eventfd_chr); } 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 pci_ivshmem_uninit(PCIDevice *dev) { IVShmemState *s = IVSHMEM(dev); if (s->migration_blocker) { migrate_del_blocker(s->migration_blocker); error_free(s->migration_blocker); } memory_region_del_subregion(&s->bar, &s->ivshmem); vmstate_unregister_ram(&s->ivshmem, DEVICE(dev)); unregister_savevm(DEVICE(dev), "ivshmem", s); }
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_write_config(PCIDevice *pdev, uint32_t address, uint32_t val, int 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 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_notify(void *opaque) { MSIVector *entry = opaque; PCIDevice *pdev = entry->pdev; IVShmemState *s = IVSHMEM(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)) { msix_notify(pdev, vector); } else { ivshmem_IntrStatus_write(s, 1); } }
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 int pci_ivshmem_init(PCIDevice *dev) { IVShmemState *s = IVSHMEM(dev); uint8_t *pci_conf; if (s->sizearg == NULL) s->ivshmem_size = 4 << 20; /* 4 MB default */ else { s->ivshmem_size = ivshmem_get_size(s); } fifo8_create(&s->incoming_fifo, sizeof(long)); register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load, dev); /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && !ivshmem_has_feature(s, IVSHMEM_MSI)) { error_report("ioeventfd/irqfd requires MSI"); exit(1); } /* check that role is reasonable */ if (s->role) { if (strncmp(s->role, "peer", 5) == 0) { s->role_val = IVSHMEM_PEER; } else if (strncmp(s->role, "master", 7) == 0) { s->role_val = IVSHMEM_MASTER; } else { error_report("'role' must be 'peer' or 'master'"); exit(1); } } else { s->role_val = IVSHMEM_MASTER; /* default */ } if (s->role_val == IVSHMEM_PEER) { error_setg(&s->migration_blocker, "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); migrate_add_blocker(s->migration_blocker); } pci_conf = dev->config; pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; pci_config_set_interrupt_pin(pci_conf, 1); s->shm_fd = 0; 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); memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size); s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_PREFETCH; if (s->ivshmem_64bit) { s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; } if ((s->server_chr != NULL) && (strncmp(s->server_chr->filename, "unix:", 5) == 0)) { /* if we get a UNIX socket as the parameter we will talk * to the ivshmem server to receive the memory region */ if (s->shmobj != NULL) { error_report("WARNING: do not specify both 'chardev' " "and 'shm' with ivshmem"); } IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", s->server_chr->filename); if (ivshmem_has_feature(s, IVSHMEM_MSI)) { ivshmem_setup_msi(s); } /* we allocate enough space for 16 guests and grow as needed */ s->nb_peers = 16; s->vm_id = -1; /* allocate/initialize space for interrupt handling */ s->peers = g_malloc0(s->nb_peers * sizeof(Peer)); pci_register_bar(dev, 2, s->ivshmem_attr, &s->bar); s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *)); qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, ivshmem_event, s); } else { /* just map the file immediately, we're not using a server */ int fd; if (s->shmobj == NULL) { error_report("Must specify 'chardev' or 'shm' to ivshmem"); exit(1); } IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); /* try opening with O_EXCL and if it succeeds zero the memory * by truncating to 0 */ if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { /* truncate file to length PCI device's memory */ if (ftruncate(fd, s->ivshmem_size) != 0) { error_report("could not truncate shared file"); } } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { error_report("could not open shared file"); exit(1); } if (check_shm_size(s, fd) == -1) { exit(1); } create_shared_memory_BAR(s, fd); } dev->config_write = ivshmem_write_config; return 0; }
static void pci_ivshmem_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM(dev); uint8_t *pci_conf; uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_PREFETCH; if (!!s->server_chr + !!s->shmobj + !!s->hostmem != 1) { error_setg(errp, "You must specify either 'shm', 'chardev' or 'x-memdev'"); return; } if (s->hostmem) { MemoryRegion *mr; if (s->sizearg) { g_warning("size argument ignored with hostmem"); } mr = host_memory_backend_get_memory(s->hostmem, errp); s->ivshmem_size = memory_region_size(mr); } else if (s->sizearg == NULL) { s->ivshmem_size = 4 << 20; /* 4 MB default */ } else { char *end; int64_t size = qemu_strtosz(s->sizearg, &end); if (size < 0 || *end != '\0' || !is_power_of_2(size)) { error_setg(errp, "Invalid size %s", s->sizearg); return; } s->ivshmem_size = size; } fifo8_create(&s->incoming_fifo, sizeof(int64_t)); /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && !ivshmem_has_feature(s, IVSHMEM_MSI)) { error_setg(errp, "ioeventfd/irqfd requires MSI"); return; } /* check that role is reasonable */ if (s->role) { if (strncmp(s->role, "peer", 5) == 0) { s->role_val = IVSHMEM_PEER; } else if (strncmp(s->role, "master", 7) == 0) { s->role_val = IVSHMEM_MASTER; } else { error_setg(errp, "'role' must be 'peer' or 'master'"); return; } } else { s->role_val = IVSHMEM_MASTER; /* default */ } if (s->role_val == IVSHMEM_PEER) { error_setg(&s->migration_blocker, "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); migrate_add_blocker(s->migration_blocker); } pci_conf = dev->config; pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; pci_config_set_interrupt_pin(pci_conf, 1); 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); memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size); if (s->ivshmem_64bit) { attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; } if (s->hostmem != NULL) { MemoryRegion *mr; IVSHMEM_DPRINTF("using hostmem\n"); mr = host_memory_backend_get_memory(MEMORY_BACKEND(s->hostmem), errp); vmstate_register_ram(mr, DEVICE(s)); memory_region_add_subregion(&s->bar, 0, mr); pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar); } else if (s->server_chr != NULL) { /* FIXME do not rely on what chr drivers put into filename */ if (strncmp(s->server_chr->filename, "unix:", 5)) { error_setg(errp, "chardev is not a unix client socket"); return; } /* if we get a UNIX socket as the parameter we will talk * to the ivshmem server to receive the memory region */ IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", s->server_chr->filename); if (ivshmem_has_feature(s, IVSHMEM_MSI) && ivshmem_setup_msi(s)) { error_setg(errp, "msix initialization failed"); return; } /* we allocate enough space for 16 peers and grow as needed */ resize_peers(s, 16); s->vm_id = -1; pci_register_bar(dev, 2, attr, &s->bar); s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *)); qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_check_version, ivshmem_event, s); } else { /* just map the file immediately, we're not using a server */ int fd; IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); /* try opening with O_EXCL and if it succeeds zero the memory * by truncating to 0 */ if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { /* truncate file to length PCI device's memory */ if (ftruncate(fd, s->ivshmem_size) != 0) { error_report("could not truncate shared file"); } } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { error_setg(errp, "could not open shared file"); return; } if (check_shm_size(s, fd, errp) == -1) { return; } create_shared_memory_BAR(s, fd, attr, errp); } }