static void host_memory_backend_init(Object *obj) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); backend->merge = qemu_opt_get_bool(qemu_get_machine_opts(), "mem-merge", true); backend->dump = qemu_opt_get_bool(qemu_get_machine_opts(), "dump-guest-core", true); backend->prealloc = mem_prealloc; object_property_add_bool(obj, "merge", host_memory_backend_get_merge, host_memory_backend_set_merge, NULL); object_property_add_bool(obj, "dump", host_memory_backend_get_dump, host_memory_backend_set_dump, NULL); object_property_add_bool(obj, "prealloc", host_memory_backend_get_prealloc, host_memory_backend_set_prealloc, NULL); object_property_add(obj, "size", "int", host_memory_backend_get_size, host_memory_backend_set_size, NULL, NULL, NULL); object_property_add(obj, "host-nodes", "int", host_memory_backend_get_host_nodes, host_memory_backend_set_host_nodes, NULL, NULL, NULL); object_property_add(obj, "policy", "str", host_memory_backend_get_policy, host_memory_backend_set_policy, NULL, NULL, NULL); }
static void host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); Error *local_err = NULL; uint64_t value; if (host_memory_backend_mr_inited(backend)) { error_setg(&local_err, "cannot change property value"); goto out; } visit_type_size(v, name, &value, &local_err); if (local_err) { goto out; } if (!value) { error_setg(&local_err, "Property '%s.%s' doesn't take value '%" PRIu64 "'", object_get_typename(obj), name, value); goto out; } backend->size = value; out: error_propagate(errp, local_err); }
static void host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint16List *host_nodes = NULL; uint16List **node = &host_nodes; unsigned long value; value = find_first_bit(backend->host_nodes, MAX_NODES); if (value == MAX_NODES) { return; } *node = g_malloc0(sizeof(**node)); (*node)->value = value; node = &(*node)->next; do { value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1); if (value == MAX_NODES) { break; } *node = g_malloc0(sizeof(**node)); (*node)->value = value; node = &(*node)->next; } while (true); visit_type_uint16List(v, name, &host_nodes, errp); }
static void host_memory_backend_set_prealloc(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); if (backend->force_prealloc) { if (value) { error_setg(errp, "remove -mem-prealloc to use the prealloc property"); return; } } if (!memory_region_size(&backend->mr)) { backend->prealloc = value; return; } if (value && !backend->prealloc) { int fd = memory_region_get_fd(&backend->mr); void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); os_mem_prealloc(fd, ptr, sz); backend->prealloc = true; } }
static void host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint16List *host_nodes = NULL; uint16List **node = &host_nodes; unsigned long value; value = find_first_bit(backend->host_nodes, MAX_NODES); node = host_memory_append_node(node, value); if (value == MAX_NODES) { goto out; } do { value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1); if (value == MAX_NODES) { break; } node = host_memory_append_node(node, value); } while (true); out: visit_type_uint16List(v, name, &host_nodes, errp); }
static void host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint64_t value = backend->size; visit_type_size(v, name, &value, errp); }
static void host_memory_backend_get_policy(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); int policy = backend->policy; visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp); }
static void memfd_backend_instance_init(Object *obj) { HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); /* default to sealed file */ m->seal = true; MEMORY_BACKEND(m)->share = true; }
static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, Object *val, Error **errp) { if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { char *path = object_get_canonical_path_component(val); error_setg(errp, "can't use already busy memdev: %s", path); g_free(path); } else { qdev_prop_allow_set_link_before_realize(obj, name, val, errp); } }
static void file_memory_backend_set_share(Object *o, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(o); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); if (host_memory_backend_mr_inited(backend)) { error_setg(errp, "cannot change property value"); return; } fb->share = value; }
static void set_mem_path(Object *o, const char *str, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(o); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); if (host_memory_backend_mr_inited(backend)) { error_setg(errp, "cannot change property value"); return; } g_free(fb->mem_path); fb->mem_path = g_strdup(str); }
static void file_backend_unparent(Object *obj) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); if (host_memory_backend_mr_inited(backend) && fb->discard_data) { void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); qemu_madvise(ptr, sz, QEMU_MADV_REMOVE); } }
static void host_memory_backend_set_policy(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); int policy; visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp); backend->policy = policy; #ifndef CONFIG_NUMA if (policy != HOST_MEM_POLICY_DEFAULT) { error_setg(errp, "NUMA policies are not supported by this QEMU"); } #endif }
static void desugar_shm(IVShmemState *s) { Object *obj; char *path; obj = object_new("memory-backend-file"); path = g_strdup_printf("/dev/shm/%s", s->shmobj); object_property_set_str(obj, path, "mem-path", &error_abort); g_free(path); object_property_set_int(obj, s->legacy_size, "size", &error_abort); object_property_set_bool(obj, true, "share", &error_abort); object_property_add_child(OBJECT(s), "internal-shm-backend", obj, &error_abort); user_creatable_complete(obj, &error_abort); s->hostmem = MEMORY_BACKEND(obj); }
static void host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { #ifdef CONFIG_NUMA HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint16List *l = NULL; visit_type_uint16List(v, name, &l, errp); while (l) { bitmap_set(backend->host_nodes, l->value, 1); l = l->next; } #else error_setg(errp, "NUMA node binding are not supported by this QEMU"); #endif }
static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); if (!memory_region_size(&backend->mr)) { backend->dump = value; return; } if (value != backend->dump) { void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); qemu_madvise(ptr, sz, value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP); backend->dump = value; } }
static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, Object *val, Error **errp) { MemoryRegion *mr; Error *local_err = NULL; mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &local_err); if (local_err) { goto out; } if (memory_region_is_mapped(mr)) { char *path = object_get_canonical_path_component(val); error_setg(&local_err, "can't use already busy memdev: %s", path); g_free(path); } else { qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err); } out: error_propagate(errp, local_err); }
static void host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(uc); HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); Error *local_err = NULL; void *ptr; uint64_t sz; if (bc->alloc) { bc->alloc(backend, &local_err); if (local_err) { error_propagate(errp, local_err); return; } ptr = memory_region_get_ram_ptr(&backend->mr); sz = memory_region_size(&backend->mr); if (backend->merge) { qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); } if (!backend->dump) { qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP); } #ifdef CONFIG_NUMA unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES); /* lastbit == MAX_NODES means maxnode = 0 */ unsigned long maxnode = (lastbit + 1) % (MAX_NODES + 1); /* ensure policy won't be ignored in case memory is preallocated * before mbind(). note: MPOL_MF_STRICT is ignored on hugepages so * this doesn't catch hugepage case. */ unsigned flags = MPOL_MF_STRICT | MPOL_MF_MOVE; /* check for invalid host-nodes and policies and give more verbose * error messages than mbind(). */ if (maxnode && backend->policy == MPOL_DEFAULT) { error_setg(errp, "host-nodes must be empty for policy default," " or you should explicitly specify a policy other" " than default"); return; } else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) { error_setg(errp, "host-nodes must be set for policy %s", HostMemPolicy_lookup[backend->policy]); return; } /* We can have up to MAX_NODES nodes, but we need to pass maxnode+1 * as argument to mbind() due to an old Linux bug (feature?) which * cuts off the last specified node. This means backend->host_nodes * must have MAX_NODES+1 bits available. */ assert(sizeof(backend->host_nodes) >= BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long)); assert(maxnode <= MAX_NODES); if (mbind(ptr, sz, backend->policy, maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) { error_setg_errno(errp, errno, "cannot bind memory to host NUMA nodes"); return; } #endif /* Preallocate memory after the NUMA policy has been instantiated. * This is necessary to guarantee memory is allocated with * specified NUMA policy in place. */ if (backend->prealloc) { os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz); } } }
static bool host_memory_backend_get_dump(Object *obj, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); return backend->dump; }
visit_type_uint16List(v, name, &l, errp); while (l) { bitmap_set(backend->host_nodes, l->value, 1); l = l->next; } #else error_setg(errp, "NUMA node binding are not supported by this QEMU"); #endif } static int host_memory_backend_get_policy(Object *obj, Error **errp G_GNUC_UNUSED) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); return backend->policy; } static void host_memory_backend_set_policy(Object *obj, int policy, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); backend->policy = policy; #ifndef CONFIG_NUMA if (policy != HOST_MEM_POLICY_DEFAULT) { error_setg(errp, "NUMA policies are not supported by this QEMU"); } #endif }
static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); return backend->prealloc || backend->force_prealloc; }
static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) { uint16_t nodenr; uint16List *cpus = NULL; if (node->has_nodeid) { nodenr = node->nodeid; } else { nodenr = nb_numa_nodes; } if (nodenr >= MAX_NODES) { error_setg(errp, "Max number of NUMA nodes reached: %" PRIu16 "", nodenr); return; } if (numa_info[nodenr].present) { error_setg(errp, "Duplicate NUMA nodeid: %" PRIu16, nodenr); return; } for (cpus = node->cpus; cpus; cpus = cpus->next) { if (cpus->value > MAX_CPUMASK_BITS) { error_setg(errp, "CPU number %" PRIu16 " is bigger than %d", cpus->value, MAX_CPUMASK_BITS); return; } bitmap_set(numa_info[nodenr].node_cpu, cpus->value, 1); } if (node->has_mem && node->has_memdev) { error_setg(errp, "qemu: cannot specify both mem= and memdev="); return; } if (have_memdevs == -1) { have_memdevs = node->has_memdev; } if (node->has_memdev != have_memdevs) { error_setg(errp, "qemu: memdev option must be specified for either " "all or no nodes"); return; } if (node->has_mem) { uint64_t mem_size = node->mem; const char *mem_str = qemu_opt_get(opts, "mem"); /* Fix up legacy suffix-less format */ if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) { mem_size <<= 20; } numa_info[nodenr].node_mem = mem_size; } if (node->has_memdev) { Object *o; o = object_resolve_path_type(node->memdev, TYPE_MEMORY_BACKEND, NULL); if (!o) { error_setg(errp, "memdev=%s is ambiguous", node->memdev); return; } object_ref(o); numa_info[nodenr].node_mem = object_property_get_int(o, "size", NULL); numa_info[nodenr].node_memdev = MEMORY_BACKEND(o); } numa_info[nodenr].present = true; max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1); }
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); } }