Reorganize the module to put all the -numa argument processing code together after the IOThread to form a logical order of processing for qemuBuildCommandLine working top down in the module This includes moving the memory cell/dimm and generic object code. Signed-off-by: John Ferlan <jferlan@xxxxxxxxxx> --- src/qemu/qemu_command.c | 4216 ++++++++++++++++++++++++----------------------- 1 file changed, 2119 insertions(+), 2097 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 3000e8f..af4e63f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1071,6 +1071,9 @@ qemuBuildIOThreadCommandLine(virCommandPtr cmd, } +/* Generic -object builder for dynamic memory cell/dimm (with -numa) or + * the RNG backend + */ static int qemuBuildObjectCommandLinePropsInternal(const char *key, const virJSONValue *value, @@ -1181,2380 +1184,2565 @@ qemuBuildObjectCommandlineFromJSON(const char *type, } -static int -qemuBuildDeviceAddressStr(virBufferPtr buf, - virDomainDefPtr domainDef, - virDomainDeviceInfoPtr info, - virQEMUCapsPtr qemuCaps) +/** Start numa and memory dimm arguments */ +/** + * qemuBuildMemoryBackendStr: + * @size: size of the memory device in kibibytes + * @pagesize: size of the requested memory page in KiB, 0 for default + * @guestNode: NUMA node in the guest that the memory object will be attached + * to, or -1 if NUMA is not used in the guest + * @hostNodes: map of host nodes to alloc the memory in, NULL for default + * @autoNodeset: fallback nodeset in case of automatic numa placement + * @def: domain definition object + * @qemuCaps: qemu capabilities object + * @cfg: qemu driver config object + * @aliasPrefix: prefix string of the alias (to allow for multiple frontents) + * @id: index of the device (to construct the alias) + * @backendStr: returns the object string + * + * Formats the configuration string for the memory device backend according + * to the configuration. @pagesize and @hostNodes can be used to override the + * default source configuration, both are optional. + * + * Returns 0 on success, 1 if only the implicit memory-device-ram with no + * other configuration was used (to detect legacy configurations). Returns + * -1 in case of an error. + */ +int +qemuBuildMemoryBackendStr(unsigned long long size, + unsigned long long pagesize, + int guestNode, + virBitmapPtr userNodeset, + virBitmapPtr autoNodeset, + virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virQEMUDriverConfigPtr cfg, + const char **backendType, + virJSONValuePtr *backendProps, + bool force) { + virDomainHugePagePtr master_hugepage = NULL; + virDomainHugePagePtr hugepage = NULL; + virDomainNumatuneMemMode mode; + const long system_page_size = virGetSystemPageSizeKB(); + virNumaMemAccess memAccess = VIR_NUMA_MEM_ACCESS_DEFAULT; + size_t i; + char *mem_path = NULL; + virBitmapPtr nodemask = NULL; int ret = -1; - char *devStr = NULL; - const char *contAlias = NULL; + virJSONValuePtr props = NULL; + bool nodeSpecified = virDomainNumatuneNodeSpecified(def->numa, guestNode); - if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - size_t i; + *backendProps = NULL; + *backendType = NULL; - if (!(devStr = virDomainPCIAddressAsString(&info->addr.pci))) - goto cleanup; - for (i = 0; i < domainDef->ncontrollers; i++) { - virDomainControllerDefPtr cont = domainDef->controllers[i]; + if (guestNode >= 0) { + /* memory devices could provide a invalid guest node */ + if (guestNode >= virDomainNumaGetNodeCount(def->numa)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("can't add memory backend for guest node '%d' as " + "the guest has only '%zu' NUMA nodes configured"), + guestNode, virDomainNumaGetNodeCount(def->numa)); + return -1; + } - if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI && - cont->idx == info->addr.pci.bus) { - contAlias = cont->info.alias; - if (!contAlias) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Device alias was not set for PCI " - "controller with index %u required " - "for device at address %s"), - info->addr.pci.bus, devStr); - goto cleanup; - } + memAccess = virDomainNumaGetNodeMemoryAccessMode(def->numa, guestNode); + } + + if (virDomainNumatuneGetMode(def->numa, guestNode, &mode) < 0 && + virDomainNumatuneGetMode(def->numa, -1, &mode) < 0) + mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT; + + if (pagesize == 0) { + /* Find the huge page size we want to use */ + for (i = 0; i < def->mem.nhugepages; i++) { + bool thisHugepage = false; + + hugepage = &def->mem.hugepages[i]; + + if (!hugepage->nodemask) { + master_hugepage = hugepage; + continue; + } + + /* just find the master hugepage in case we don't use NUMA */ + if (guestNode < 0) + continue; + + if (virBitmapGetBit(hugepage->nodemask, guestNode, + &thisHugepage) < 0) { + /* Ignore this error. It's not an error after all. Well, + * the nodemask for this <page/> can contain lower NUMA + * nodes than we are querying in here. */ + continue; + } + + if (thisHugepage) { + /* Hooray, we've found the page size */ break; } } - if (!contAlias) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Could not find PCI " - "controller with index %u required " - "for device at address %s"), - info->addr.pci.bus, devStr); - goto cleanup; + + if (i == def->mem.nhugepages) { + /* We have not found specific huge page to be used with this + * NUMA node. Use the generic setting then (<page/> without any + * @nodemask) if possible. */ + hugepage = master_hugepage; } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) { - if (info->addr.pci.function != 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Only PCI device addresses with function=0 " - "are supported with this QEMU binary")); - goto cleanup; + if (hugepage) + pagesize = hugepage->size; + } + + if (pagesize == system_page_size) { + /* However, if user specified to use "huge" page + * of regular system page size, it's as if they + * hasn't specified any huge pages at all. */ + pagesize = 0; + hugepage = NULL; + } + + if (!(props = virJSONValueNewObject())) + return -1; + + if (pagesize || hugepage) { + if (pagesize) { + /* Now lets see, if the huge page we want to use is even mounted + * and ready to use */ + for (i = 0; i < cfg->nhugetlbfs; i++) { + if (cfg->hugetlbfs[i].size == pagesize) + break; } - if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("'multifunction=on' is not supported with " - "this QEMU binary")); + + if (i == cfg->nhugetlbfs) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find any usable hugetlbfs mount for %llu KiB"), + pagesize); goto cleanup; } + + if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[i]))) + goto cleanup; + } else { + if (!(mem_path = qemuGetDefaultHugepath(cfg->hugetlbfs, + cfg->nhugetlbfs))) + goto cleanup; } - if (info->addr.pci.bus != 0 && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) { + *backendType = "memory-backend-file"; + + if (virJSONValueObjectAdd(props, + "b:prealloc", true, + "s:mem-path", mem_path, + NULL) < 0) + goto cleanup; + + switch (memAccess) { + case VIR_NUMA_MEM_ACCESS_SHARED: + if (virJSONValueObjectAdd(props, "b:share", true, NULL) < 0) + goto cleanup; + break; + + case VIR_NUMA_MEM_ACCESS_PRIVATE: + if (virJSONValueObjectAdd(props, "b:share", false, NULL) < 0) + goto cleanup; + break; + + case VIR_NUMA_MEM_ACCESS_DEFAULT: + case VIR_NUMA_MEM_ACCESS_LAST: + break; + } + } else { + if (memAccess) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Multiple PCI buses are not supported " - "with this QEMU binary")); + _("Shared memory mapping is supported " + "only with hugepages")); goto cleanup; } - virBufferAsprintf(buf, ",bus=%s", contAlias); - if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON) - virBufferAddLit(buf, ",multifunction=on"); - else if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_OFF) - virBufferAddLit(buf, ",multifunction=off"); - virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot); - if (info->addr.pci.function != 0) - virBufferAsprintf(buf, ".0x%x", info->addr.pci.function); - } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { - if (!(contAlias = virDomainControllerAliasFind(domainDef, - VIR_DOMAIN_CONTROLLER_TYPE_USB, - info->addr.usb.bus))) + *backendType = "memory-backend-ram"; + } + + if (virJSONValueObjectAdd(props, "U:size", size * 1024, NULL) < 0) + goto cleanup; + + if (userNodeset) { + nodemask = userNodeset; + } else { + if (virDomainNumatuneMaybeGetNodeset(def->numa, autoNodeset, + &nodemask, guestNode) < 0) goto cleanup; - virBufferAsprintf(buf, ",bus=%s.0,port=%s", contAlias, info->addr.usb.port); - } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) { - if (info->addr.spaprvio.has_reg) - virBufferAsprintf(buf, ",reg=0x%llx", info->addr.spaprvio.reg); - } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { - if (info->addr.ccw.assigned) - virBufferAsprintf(buf, ",devno=%x.%x.%04x", - info->addr.ccw.cssid, - info->addr.ccw.ssid, - info->addr.ccw.devno); - } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA) { - virBufferAsprintf(buf, ",iobase=0x%x,irq=0x%x", - info->addr.isa.iobase, - info->addr.isa.irq); } - ret = 0; - cleanup: - VIR_FREE(devStr); - return ret; -} + if (nodemask) { + if (!virNumaNodesetIsAvailable(nodemask)) + goto cleanup; + if (virJSONValueObjectAdd(props, + "m:host-nodes", nodemask, + "S:policy", qemuNumaPolicyTypeToString(mode), + NULL) < 0) + goto cleanup; + } -static int -qemuBuildRomStr(virBufferPtr buf, - virDomainDeviceInfoPtr info, - virQEMUCapsPtr qemuCaps) -{ - if (info->rombar || info->romfile) { - if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("rombar and romfile are supported only for PCI devices")); - return -1; - } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_ROMBAR)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("rombar and romfile not supported in this QEMU binary")); - return -1; + /* If none of the following is requested... */ + if (!pagesize && !userNodeset && !memAccess && !nodeSpecified && !force) { + /* report back that using the new backend is not necessary + * to achieve the desired configuration */ + ret = 1; + } else { + /* otherwise check the required capability */ + if (STREQ(*backendType, "memory-backend-file") && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this qemu doesn't support the " + "memory-backend-file object")); + goto cleanup; + } else if (STREQ(*backendType, "memory-backend-ram") && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this qemu doesn't support the " + "memory-backend-ram object")); + goto cleanup; } - switch (info->rombar) { - case VIR_TRISTATE_SWITCH_OFF: - virBufferAddLit(buf, ",rombar=0"); - break; - case VIR_TRISTATE_SWITCH_ON: - virBufferAddLit(buf, ",rombar=1"); - break; - default: - break; - } - if (info->romfile) - virBufferAsprintf(buf, ",romfile=%s", info->romfile); + ret = 0; } - return 0; -} -static int -qemuBuildIoEventFdStr(virBufferPtr buf, - virTristateSwitch use, - virQEMUCapsPtr qemuCaps) -{ - if (use && virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_IOEVENTFD)) - virBufferAsprintf(buf, ",ioeventfd=%s", - virTristateSwitchTypeToString(use)); - return 0; + *backendProps = props; + props = NULL; + + cleanup: + virJSONValueFree(props); + VIR_FREE(mem_path); + + return ret; } -#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ " static int -qemuSafeSerialParamValue(const char *value) +qemuBuildMemoryCellBackendStr(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virQEMUDriverConfigPtr cfg, + size_t cell, + virBitmapPtr auto_nodeset, + char **backendStr) { - if (strspn(value, QEMU_SERIAL_PARAM_ACCEPTED_CHARS) != strlen(value)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("driver serial '%s' contains unsafe characters"), - value); - return -1; - } + virJSONValuePtr props = NULL; + char *alias = NULL; + const char *backendType; + int ret = -1; + int rc; + unsigned long long memsize = virDomainNumaGetNodeMemorySize(def->numa, + cell); - return 0; + *backendStr = NULL; + + if (virAsprintf(&alias, "ram-node%zu", cell) < 0) + goto cleanup; + + if ((rc = qemuBuildMemoryBackendStr(memsize, 0, cell, NULL, auto_nodeset, + def, qemuCaps, cfg, &backendType, + &props, false)) < 0) + goto cleanup; + + if (!(*backendStr = qemuBuildObjectCommandlineFromJSON(backendType, + alias, + props))) + goto cleanup; + + ret = rc; + + cleanup: + VIR_FREE(alias); + virJSONValueFree(props); + + return ret; } + static char * -qemuGetSecretString(virConnectPtr conn, - const char *scheme, - bool encoded, - virStorageAuthDefPtr authdef, - virSecretUsageType secretUsageType) +qemuBuildMemoryDimmBackendStr(virDomainMemoryDefPtr mem, + virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virQEMUDriverConfigPtr cfg) { - size_t secret_size; - virSecretPtr sec = NULL; - char *secret = NULL; - char uuidStr[VIR_UUID_STRING_BUFLEN]; + virJSONValuePtr props = NULL; + char *alias = NULL; + const char *backendType; + char *ret = NULL; - /* look up secret */ - switch (authdef->secretType) { - case VIR_STORAGE_SECRET_TYPE_UUID: - sec = virSecretLookupByUUID(conn, authdef->secret.uuid); - virUUIDFormat(authdef->secret.uuid, uuidStr); - break; - case VIR_STORAGE_SECRET_TYPE_USAGE: - sec = virSecretLookupByUsage(conn, secretUsageType, - authdef->secret.usage); - break; + if (!mem->info.alias) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("memory device alias is not assigned")); + return NULL; } - if (!sec) { - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virReportError(VIR_ERR_NO_SECRET, - _("%s no secret matches uuid '%s'"), - scheme, uuidStr); - } else { - virReportError(VIR_ERR_NO_SECRET, - _("%s no secret matches usage value '%s'"), - scheme, authdef->secret.usage); - } + if (virAsprintf(&alias, "mem%s", mem->info.alias) < 0) goto cleanup; - } - secret = (char *)conn->secretDriver->secretGetValue(sec, &secret_size, 0, - VIR_SECRET_GET_VALUE_INTERNAL_CALL); - if (!secret) { - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("could not get value of the secret for " - "username '%s' using uuid '%s'"), - authdef->username, uuidStr); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("could not get value of the secret for " - "username '%s' using usage value '%s'"), - authdef->username, authdef->secret.usage); - } + if (qemuBuildMemoryBackendStr(mem->size, mem->pagesize, + mem->targetNode, mem->sourceNodes, NULL, + def, qemuCaps, cfg, + &backendType, &props, true) < 0) goto cleanup; - } - - if (encoded) { - char *base64 = NULL; - base64_encode_alloc(secret, secret_size, &base64); - VIR_FREE(secret); - if (!base64) { - virReportOOMError(); - goto cleanup; - } - secret = base64; - } + ret = qemuBuildObjectCommandlineFromJSON(backendType, alias, props); cleanup: - virObjectUnref(sec); - return secret; + VIR_FREE(alias); + virJSONValueFree(props); + + return ret; } -static int -qemuNetworkDriveGetPort(int protocol, - const char *port) +char * +qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem) { - int ret = 0; - - if (port) { - if (virStrToLong_i(port, NULL, 10, &ret) < 0 || ret < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("failed to parse port number '%s'"), - port); - return -1; - } + virBuffer buf = VIR_BUFFER_INITIALIZER; - return ret; + if (!mem->info.alias) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing alias for memory device")); + return NULL; } - switch ((virStorageNetProtocol) protocol) { - case VIR_STORAGE_NET_PROTOCOL_HTTP: - return 80; - - case VIR_STORAGE_NET_PROTOCOL_HTTPS: - return 443; - - case VIR_STORAGE_NET_PROTOCOL_FTP: - return 21; + switch ((virDomainMemoryModel) mem->model) { + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + virBufferAddLit(&buf, "pc-dimm,"); - case VIR_STORAGE_NET_PROTOCOL_FTPS: - return 990; + if (mem->targetNode >= 0) + virBufferAsprintf(&buf, "node=%d,", mem->targetNode); - case VIR_STORAGE_NET_PROTOCOL_TFTP: - return 69; + virBufferAsprintf(&buf, "memdev=mem%s,id=%s", + mem->info.alias, mem->info.alias); - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - return 7000; + if (mem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM) { + virBufferAsprintf(&buf, ",slot=%d", mem->info.addr.dimm.slot); + virBufferAsprintf(&buf, ",addr=%llu", mem->info.addr.dimm.base); + } - case VIR_STORAGE_NET_PROTOCOL_NBD: - return 10809; + break; - case VIR_STORAGE_NET_PROTOCOL_ISCSI: - case VIR_STORAGE_NET_PROTOCOL_GLUSTER: - /* no default port specified */ - return 0; + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("invalid memory device type")); + break; - case VIR_STORAGE_NET_PROTOCOL_RBD: - case VIR_STORAGE_NET_PROTOCOL_LAST: - case VIR_STORAGE_NET_PROTOCOL_NONE: - /* not applicable */ - return -1; } - return -1; -} + if (virBufferCheckError(&buf) < 0) + return NULL; -#define QEMU_DEFAULT_NBD_PORT "10809" + return virBufferContentAndReset(&buf); +} -static char * -qemuBuildNetworkDriveURI(virStorageSourcePtr src, - const char *username, - const char *secret) + +static int +qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg, + virDomainDefPtr def, + virCommandPtr cmd, + virQEMUCapsPtr qemuCaps, + virBitmapPtr auto_nodeset) { - char *ret = NULL; - virBuffer buf = VIR_BUFFER_INITIALIZER; - virURIPtr uri = NULL; size_t i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *cpumask = NULL, *tmpmask = NULL, *next = NULL; + char **nodeBackends = NULL; + bool needBackend = false; + int rc; + int ret = -1; + size_t ncells = virDomainNumaGetNodeCount(def->numa); + const long system_page_size = virGetSystemPageSizeKB(); - switch ((virStorageNetProtocol) src->protocol) { - case VIR_STORAGE_NET_PROTOCOL_NBD: - if (src->nhosts != 1) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("protocol '%s' accepts only one host"), - virStorageNetProtocolTypeToString(src->protocol)); - goto cleanup; - } - - if (!((src->hosts->name && strchr(src->hosts->name, ':')) || - (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP && - !src->hosts->name) || - (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX && - src->hosts->socket && - src->hosts->socket[0] != '/'))) { + if (virDomainNumatuneHasPerNodeBinding(def->numa) && + !(virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) || + virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Per-node memory binding is not supported " + "with this QEMU")); + goto cleanup; + } - virBufferAddLit(&buf, "nbd:"); + if (def->mem.nhugepages && + def->mem.hugepages[0].size != system_page_size && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("huge pages per NUMA node are not " + "supported with this QEMU")); + goto cleanup; + } - switch (src->hosts->transport) { - case VIR_STORAGE_NET_HOST_TRANS_TCP: - virBufferStrcat(&buf, src->hosts->name, NULL); - virBufferAsprintf(&buf, ":%s", - src->hosts->port ? src->hosts->port : - QEMU_DEFAULT_NBD_PORT); - break; + if (!virDomainNumatuneNodesetIsAvailable(def->numa, auto_nodeset)) + goto cleanup; - case VIR_STORAGE_NET_HOST_TRANS_UNIX: - if (!src->hosts->socket) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("socket attribute required for " - "unix transport")); - goto cleanup; - } + for (i = 0; i < def->mem.nhugepages; i++) { + ssize_t next_bit, pos = 0; - virBufferAsprintf(&buf, "unix:%s", src->hosts->socket); - break; + if (!def->mem.hugepages[i].nodemask) { + /* This is the master hugepage to use. Skip it as it has no + * nodemask anyway. */ + continue; + } - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("nbd does not support transport '%s'"), - virStorageNetHostTransportTypeToString(src->hosts->transport)); - goto cleanup; - } + if (ncells) { + /* Fortunately, we allow only guest NUMA nodes to be continuous + * starting from zero. */ + pos = ncells - 1; + } - if (src->path) - virBufferAsprintf(&buf, ":exportname=%s", src->path); + next_bit = virBitmapNextSetBit(def->mem.hugepages[i].nodemask, pos); + if (next_bit >= 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("hugepages: node %zd not found"), + next_bit); + goto cleanup; + } + } - if (virBufferCheckError(&buf) < 0) - goto cleanup; + if (VIR_ALLOC_N(nodeBackends, ncells) < 0) + goto cleanup; - ret = virBufferContentAndReset(&buf); + /* using of -numa memdev= cannot be combined with -numa mem=, thus we + * need to check which approach to use */ + for (i = 0; i < ncells; i++) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) || + virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) { + if ((rc = qemuBuildMemoryCellBackendStr(def, qemuCaps, cfg, i, + auto_nodeset, + &nodeBackends[i])) < 0) goto cleanup; - } - /* fallthrough */ - /* NBD code uses same formatting scheme as others in some cases */ - case VIR_STORAGE_NET_PROTOCOL_HTTP: - case VIR_STORAGE_NET_PROTOCOL_HTTPS: - case VIR_STORAGE_NET_PROTOCOL_FTP: - case VIR_STORAGE_NET_PROTOCOL_FTPS: - case VIR_STORAGE_NET_PROTOCOL_TFTP: - case VIR_STORAGE_NET_PROTOCOL_ISCSI: - case VIR_STORAGE_NET_PROTOCOL_GLUSTER: - if (src->nhosts != 1) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("protocol '%s' accepts only one host"), - virStorageNetProtocolTypeToString(src->protocol)); + if (rc == 0) + needBackend = true; + } else { + if (virDomainNumaGetNodeMemoryAccessMode(def->numa, i)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Shared memory mapping is not supported " + "with this QEMU")); goto cleanup; } + } + } - if (VIR_ALLOC(uri) < 0) - goto cleanup; + if (!needBackend && + qemuBuildMemPathStr(cfg, def, qemuCaps, cmd) < 0) + goto cleanup; - if (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP) { - if (VIR_STRDUP(uri->scheme, - virStorageNetProtocolTypeToString(src->protocol)) < 0) - goto cleanup; - } else { - if (virAsprintf(&uri->scheme, "%s+%s", - virStorageNetProtocolTypeToString(src->protocol), - virStorageNetHostTransportTypeToString(src->hosts->transport)) < 0) - goto cleanup; - } + for (i = 0; i < ncells; i++) { + VIR_FREE(cpumask); + if (!(cpumask = + virBitmapFormat(virDomainNumaGetNodeCpumask(def->numa, i)))) + goto cleanup; - if ((uri->port = qemuNetworkDriveGetPort(src->protocol, src->hosts->port)) < 0) - goto cleanup; + if (strchr(cpumask, ',') && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("disjoint NUMA cpu ranges are not supported " + "with this QEMU")); + goto cleanup; + } - if (src->path) { - if (src->volume) { - if (virAsprintf(&uri->path, "/%s%s", - src->volume, src->path) < 0) - goto cleanup; - } else { - if (virAsprintf(&uri->path, "%s%s", - src->path[0] == '/' ? "" : "/", - src->path) < 0) - goto cleanup; - } - } + if (needBackend) + virCommandAddArgList(cmd, "-object", nodeBackends[i], NULL); - if (src->hosts->socket && - virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0) - goto cleanup; + virCommandAddArg(cmd, "-numa"); + virBufferAsprintf(&buf, "node,nodeid=%zu", i); - if (username) { - if (secret) { - if (virAsprintf(&uri->user, "%s:%s", username, secret) < 0) - goto cleanup; - } else { - if (VIR_STRDUP(uri->user, username) < 0) - goto cleanup; - } - } + for (tmpmask = cpumask; tmpmask; tmpmask = next) { + if ((next = strchr(tmpmask, ','))) + *(next++) = '\0'; + virBufferAddLit(&buf, ",cpus="); + virBufferAdd(&buf, tmpmask, -1); + } - if (VIR_STRDUP(uri->server, src->hosts->name) < 0) - goto cleanup; + if (needBackend) + virBufferAsprintf(&buf, ",memdev=ram-node%zu", i); + else + virBufferAsprintf(&buf, ",mem=%llu", + virDomainNumaGetNodeMemorySize(def->numa, i) / 1024); - ret = virURIFormat(uri); + virCommandAddArgBuffer(cmd, &buf); + } + ret = 0; - break; + cleanup: + VIR_FREE(cpumask); - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - if (!src->path) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing disk source for 'sheepdog' protocol")); - goto cleanup; - } + if (nodeBackends) { + for (i = 0; i < ncells; i++) + VIR_FREE(nodeBackends[i]); - if (src->nhosts == 0) { - if (virAsprintf(&ret, "sheepdog:%s", src->path) < 0) - goto cleanup; - } else if (src->nhosts == 1) { - if (virAsprintf(&ret, "sheepdog:%s:%s:%s", - src->hosts->name, - src->hosts->port ? src->hosts->port : "7000", - src->path) < 0) - goto cleanup; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("protocol 'sheepdog' accepts up to one host")); - goto cleanup; - } + VIR_FREE(nodeBackends); + } - break; + virBufferFreeAndReset(&buf); + return ret; +} - case VIR_STORAGE_NET_PROTOCOL_RBD: - if (strchr(src->path, ':')) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("':' not allowed in RBD source volume name '%s'"), - src->path); - goto cleanup; - } - virBufferStrcat(&buf, "rbd:", src->path, NULL); +static int +qemuBuildNumaCommandLine(virCommandPtr cmd, + virQEMUDriverConfigPtr cfg, + virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virBitmapPtr nodeset) +{ + size_t i; - if (src->snapshot) - virBufferEscape(&buf, '\\', ":", "@%s", src->snapshot); - - if (username) { - virBufferEscape(&buf, '\\', ":", ":id=%s", username); - virBufferEscape(&buf, '\\', ":", - ":key=%s:auth_supported=cephx\\;none", - secret); - } else { - virBufferAddLit(&buf, ":auth_supported=none"); - } - - if (src->nhosts > 0) { - virBufferAddLit(&buf, ":mon_host="); - for (i = 0; i < src->nhosts; i++) { - if (i) - virBufferAddLit(&buf, "\\;"); - - /* assume host containing : is ipv6 */ - if (strchr(src->hosts[i].name, ':')) - virBufferEscape(&buf, '\\', ":", "[%s]", - src->hosts[i].name); - else - virBufferAsprintf(&buf, "%s", src->hosts[i].name); - - if (src->hosts[i].port) - virBufferAsprintf(&buf, "\\:%s", src->hosts[i].port); - } - } + if (virDomainNumaGetNodeCount(def->numa) && + qemuBuildNumaArgStr(cfg, def, cmd, qemuCaps, nodeset) < 0) + goto error; - if (src->configFile) - virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile); + /* memory hotplug requires NUMA to be enabled - we already checked + * that memory devices are present only when NUMA is */ + for (i = 0; i < def->nmems; i++) { + char *backStr; + char *dimmStr; - if (virBufferCheckError(&buf) < 0) - goto cleanup; + if (!(backStr = qemuBuildMemoryDimmBackendStr(def->mems[i], def, + qemuCaps, cfg))) + goto error; - ret = virBufferContentAndReset(&buf); - break; + if (!(dimmStr = qemuBuildMemoryDeviceStr(def->mems[i]))) { + VIR_FREE(backStr); + goto error; + } + virCommandAddArgList(cmd, "-object", backStr, "-device", dimmStr, NULL); - case VIR_STORAGE_NET_PROTOCOL_LAST: - case VIR_STORAGE_NET_PROTOCOL_NONE: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unexpected network protocol '%s'"), - virStorageNetProtocolTypeToString(src->protocol)); - goto cleanup; + VIR_FREE(backStr); + VIR_FREE(dimmStr); } - cleanup: - virBufferFreeAndReset(&buf); - virURIFree(uri); + return 0; - return ret; + error: + return -1; } -int -qemuGetDriveSourceString(virStorageSourcePtr src, - virConnectPtr conn, - char **source) +static int +qemuBuildDeviceAddressStr(virBufferPtr buf, + virDomainDefPtr domainDef, + virDomainDeviceInfoPtr info, + virQEMUCapsPtr qemuCaps) { - int actualType = virStorageSourceGetActualType(src); - char *secret = NULL; - char *username = NULL; int ret = -1; + char *devStr = NULL; + const char *contAlias = NULL; - *source = NULL; - - /* return 1 for empty sources */ - if (virStorageSourceIsEmpty(src)) - return 1; + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + size_t i; - if (conn) { - if (actualType == VIR_STORAGE_TYPE_NETWORK && - src->auth && - (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI || - src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { - bool encode = false; - int secretType = VIR_SECRET_USAGE_TYPE_ISCSI; - const char *protocol = virStorageNetProtocolTypeToString(src->protocol); - username = src->auth->username; + if (!(devStr = virDomainPCIAddressAsString(&info->addr.pci))) + goto cleanup; + for (i = 0; i < domainDef->ncontrollers; i++) { + virDomainControllerDefPtr cont = domainDef->controllers[i]; - if (src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) { - /* qemu requires the secret to be encoded for RBD */ - encode = true; - secretType = VIR_SECRET_USAGE_TYPE_CEPH; + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI && + cont->idx == info->addr.pci.bus) { + contAlias = cont->info.alias; + if (!contAlias) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Device alias was not set for PCI " + "controller with index %u required " + "for device at address %s"), + info->addr.pci.bus, devStr); + goto cleanup; + } + break; } + } + if (!contAlias) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find PCI " + "controller with index %u required " + "for device at address %s"), + info->addr.pci.bus, devStr); + goto cleanup; + } - if (!(secret = qemuGetSecretString(conn, - protocol, - encode, - src->auth, - secretType))) + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) { + if (info->addr.pci.function != 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only PCI device addresses with function=0 " + "are supported with this QEMU binary")); + goto cleanup; + } + if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'multifunction=on' is not supported with " + "this QEMU binary")); goto cleanup; + } } - } - switch ((virStorageType) actualType) { - case VIR_STORAGE_TYPE_BLOCK: - case VIR_STORAGE_TYPE_FILE: - case VIR_STORAGE_TYPE_DIR: - if (VIR_STRDUP(*source, src->path) < 0) + if (info->addr.pci.bus != 0 && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Multiple PCI buses are not supported " + "with this QEMU binary")); goto cleanup; + } + virBufferAsprintf(buf, ",bus=%s", contAlias); - break; - - case VIR_STORAGE_TYPE_NETWORK: - if (!(*source = qemuBuildNetworkDriveURI(src, username, secret))) + if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON) + virBufferAddLit(buf, ",multifunction=on"); + else if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_OFF) + virBufferAddLit(buf, ",multifunction=off"); + virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot); + if (info->addr.pci.function != 0) + virBufferAsprintf(buf, ".0x%x", info->addr.pci.function); + } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { + if (!(contAlias = virDomainControllerAliasFind(domainDef, + VIR_DOMAIN_CONTROLLER_TYPE_USB, + info->addr.usb.bus))) goto cleanup; - break; - - case VIR_STORAGE_TYPE_VOLUME: - case VIR_STORAGE_TYPE_NONE: - case VIR_STORAGE_TYPE_LAST: - break; + virBufferAsprintf(buf, ",bus=%s.0,port=%s", contAlias, info->addr.usb.port); + } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) { + if (info->addr.spaprvio.has_reg) + virBufferAsprintf(buf, ",reg=0x%llx", info->addr.spaprvio.reg); + } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { + if (info->addr.ccw.assigned) + virBufferAsprintf(buf, ",devno=%x.%x.%04x", + info->addr.ccw.cssid, + info->addr.ccw.ssid, + info->addr.ccw.devno); + } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA) { + virBufferAsprintf(buf, ",iobase=0x%x,irq=0x%x", + info->addr.isa.iobase, + info->addr.isa.irq); } ret = 0; - cleanup: - VIR_FREE(secret); + VIR_FREE(devStr); return ret; } - -/* Perform disk definition config validity checks */ -int -qemuCheckDiskConfig(virDomainDiskDefPtr disk) +static int +qemuBuildRomStr(virBufferPtr buf, + virDomainDeviceInfoPtr info, + virQEMUCapsPtr qemuCaps) { - if (virDiskNameToIndex(disk->dst) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } + if (info->rombar || info->romfile) { + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("rombar and romfile are supported only for PCI devices")); + return -1; + } + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_ROMBAR)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("rombar and romfile not supported in this QEMU binary")); + return -1; + } - if (disk->wwn) { - if ((disk->bus != VIR_DOMAIN_DISK_BUS_IDE) && - (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Only ide and scsi disk support wwn")); - goto error; + switch (info->rombar) { + case VIR_TRISTATE_SWITCH_OFF: + virBufferAddLit(buf, ",rombar=0"); + break; + case VIR_TRISTATE_SWITCH_ON: + virBufferAddLit(buf, ",rombar=1"); + break; + default: + break; } + if (info->romfile) + virBufferAsprintf(buf, ",romfile=%s", info->romfile); } + return 0; +} - if ((disk->vendor || disk->product) && - disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Only scsi disk supports vendor and product")); - goto error; - } +static int +qemuBuildIoEventFdStr(virBufferPtr buf, + virTristateSwitch use, + virQEMUCapsPtr qemuCaps) +{ + if (use && virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_IOEVENTFD)) + virBufferAsprintf(buf, ",ioeventfd=%s", + virTristateSwitchTypeToString(use)); + return 0; +} - if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { - /* make sure that both the bus supports type='lun' (SG_IO). */ - if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO && - disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk device='lun' is not supported for bus='%s'"), - virDomainDiskQEMUBusTypeToString(disk->bus)); - goto error; - } - if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) { - if (disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_ISCSI) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk device='lun' is not supported " - "for protocol='%s'"), - virStorageNetProtocolTypeToString(disk->src->protocol)); - goto error; - } - } else if (!virDomainDiskSourceIsBlockType(disk->src, true)) { - goto error; - } - if (disk->wwn) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Setting wwn is not supported for lun device")); - goto error; - } - if (disk->vendor || disk->product) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Setting vendor or product is not supported " - "for lun device")); - goto error; - } +#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ " + +static int +qemuSafeSerialParamValue(const char *value) +{ + if (strspn(value, QEMU_SERIAL_PARAM_ACCEPTED_CHARS) != strlen(value)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("driver serial '%s' contains unsafe characters"), + value); + return -1; } + return 0; - error: - return -1; } - -/* Check whether the device address is using either 'ccw' or default s390 - * address format and whether that's "legal" for the current qemu and/or - * guest os.machine type. This is the corollary to the code which doesn't - * find the address type set using an emulator that supports either 'ccw' - * or s390 and sets the address type based on the capabilities. - * - * If the address is using 'ccw' or s390 and it's not supported, generate - * an error and return false; otherwise, return true. - */ -bool -qemuCheckCCWS390AddressSupport(virDomainDefPtr def, - virDomainDeviceInfo info, - virQEMUCapsPtr qemuCaps, - const char *devicename) +static char * +qemuGetSecretString(virConnectPtr conn, + const char *scheme, + bool encoded, + virStorageAuthDefPtr authdef, + virSecretUsageType secretUsageType) { - if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { - if (!qemuDomainMachineIsS390CCW(def)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("cannot use CCW address type for device " - "'%s' using machine type '%s'"), - devicename, def->os.machine); - return false; - } else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("CCW address type is not supported by " - "this QEMU")); - return false; - } - } else if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("virtio S390 address type is not supported by " - "this QEMU")); - return false; - } + size_t secret_size; + virSecretPtr sec = NULL; + char *secret = NULL; + char uuidStr[VIR_UUID_STRING_BUFLEN]; + + /* look up secret */ + switch (authdef->secretType) { + case VIR_STORAGE_SECRET_TYPE_UUID: + sec = virSecretLookupByUUID(conn, authdef->secret.uuid); + virUUIDFormat(authdef->secret.uuid, uuidStr); + break; + case VIR_STORAGE_SECRET_TYPE_USAGE: + sec = virSecretLookupByUsage(conn, secretUsageType, + authdef->secret.usage); + break; } - return true; -} + if (!sec) { + if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { + virReportError(VIR_ERR_NO_SECRET, + _("%s no secret matches uuid '%s'"), + scheme, uuidStr); + } else { + virReportError(VIR_ERR_NO_SECRET, + _("%s no secret matches usage value '%s'"), + scheme, authdef->secret.usage); + } + goto cleanup; + } -/* Qemu 1.2 and later have a binary flag -enable-fips that must be - * used for VNC auth to obey FIPS settings; but the flag only - * exists on Linux, and with no way to probe for it via QMP. Our - * solution: if FIPS mode is required, then unconditionally use - * the flag, regardless of qemu version, for the following matrix: - * - * old QEMU new QEMU - * FIPS enabled doesn't start VNC auth disabled - * FIPS disabled/missing VNC auth enabled VNC auth enabled - */ -bool -qemuCheckFips(void) -{ - bool ret = false; + secret = (char *)conn->secretDriver->secretGetValue(sec, &secret_size, 0, + VIR_SECRET_GET_VALUE_INTERNAL_CALL); + if (!secret) { + if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("could not get value of the secret for " + "username '%s' using uuid '%s'"), + authdef->username, uuidStr); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("could not get value of the secret for " + "username '%s' using usage value '%s'"), + authdef->username, authdef->secret.usage); + } + goto cleanup; + } - if (virFileExists("/proc/sys/crypto/fips_enabled")) { - char *buf = NULL; + if (encoded) { + char *base64 = NULL; - if (virFileReadAll("/proc/sys/crypto/fips_enabled", 10, &buf) < 0) - return ret; - if (STREQ(buf, "1\n")) - ret = true; - VIR_FREE(buf); + base64_encode_alloc(secret, secret_size, &base64); + VIR_FREE(secret); + if (!base64) { + virReportOOMError(); + goto cleanup; + } + secret = base64; } - return ret; + cleanup: + virObjectUnref(sec); + return secret; } -char * -qemuBuildDriveStr(virConnectPtr conn, - virDomainDiskDefPtr disk, - bool bootable, - virQEMUCapsPtr qemuCaps) +static int +qemuNetworkDriveGetPort(int protocol, + const char *port) { - virBuffer opt = VIR_BUFFER_INITIALIZER; - const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); - const char *trans = - virDomainDiskGeometryTransTypeToString(disk->geometry.trans); - int idx = virDiskNameToIndex(disk->dst); - int busid = -1, unitid = -1; - char *source = NULL; - int actualType = virStorageSourceGetActualType(disk->src); - - if (idx < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } - - switch (disk->bus) { - case VIR_DOMAIN_DISK_BUS_SCSI: - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected address type for scsi disk")); - goto error; - } - - /* Setting bus= attr for SCSI drives, causes a controller - * to be created. Yes this is slightly odd. It is not possible - * to have > 1 bus on a SCSI controller (yet). */ - if (disk->info.addr.drive.bus != 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("SCSI controller only supports 1 bus")); - goto error; - } - busid = disk->info.addr.drive.controller; - unitid = disk->info.addr.drive.unit; - break; + int ret = 0; - case VIR_DOMAIN_DISK_BUS_IDE: - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected address type for ide disk")); - goto error; - } - /* We can only have 1 IDE controller (currently) */ - if (disk->info.addr.drive.controller != 0) { + if (port) { + if (virStrToLong_i(port, NULL, 10, &ret) < 0 || ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Only 1 %s controller is supported"), bus); - goto error; + _("failed to parse port number '%s'"), + port); + return -1; } - busid = disk->info.addr.drive.bus; - unitid = disk->info.addr.drive.unit; - break; - case VIR_DOMAIN_DISK_BUS_FDC: - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected address type for fdc disk")); - goto error; - } - /* We can only have 1 FDC controller (currently) */ - if (disk->info.addr.drive.controller != 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Only 1 %s controller is supported"), bus); - goto error; - } - /* We can only have 1 FDC bus (currently) */ - if (disk->info.addr.drive.bus != 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Only 1 %s bus is supported"), bus); - goto error; - } - if (disk->info.addr.drive.target != 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("target must be 0 for controller fdc")); - goto error; - } - unitid = disk->info.addr.drive.unit; + return ret; + } - break; + switch ((virStorageNetProtocol) protocol) { + case VIR_STORAGE_NET_PROTOCOL_HTTP: + return 80; - case VIR_DOMAIN_DISK_BUS_VIRTIO: - idx = -1; - break; + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + return 443; - case VIR_DOMAIN_DISK_BUS_XEN: - case VIR_DOMAIN_DISK_BUS_SD: - /* Xen and SD have no address type currently, so assign - * based on index */ - break; - } + case VIR_STORAGE_NET_PROTOCOL_FTP: + return 21; - if (qemuGetDriveSourceString(disk->src, conn, &source) < 0) - goto error; + case VIR_STORAGE_NET_PROTOCOL_FTPS: + return 990; - if (source && - !((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || - disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) && - disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { + case VIR_STORAGE_NET_PROTOCOL_TFTP: + return 69; - virBufferAddLit(&opt, "file="); + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + return 7000; - switch (actualType) { - case VIR_STORAGE_TYPE_DIR: - /* QEMU only supports magic FAT format for now */ - if (disk->src->format > 0 && - disk->src->format != VIR_STORAGE_FILE_FAT) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk driver type for '%s'"), - virStorageFileFormatTypeToString(disk->src->format)); - goto error; - } + case VIR_STORAGE_NET_PROTOCOL_NBD: + return 10809; - if (!disk->src->readonly) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot create virtual FAT disks in read-write mode")); - goto error; - } + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + /* no default port specified */ + return 0; - virBufferAddLit(&opt, "fat:"); + case VIR_STORAGE_NET_PROTOCOL_RBD: + case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: + /* not applicable */ + return -1; + } - if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) - virBufferAddLit(&opt, "floppy:"); + return -1; +} - break; +#define QEMU_DEFAULT_NBD_PORT "10809" - case VIR_STORAGE_TYPE_BLOCK: - if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - disk->src->type == VIR_STORAGE_TYPE_VOLUME ? - _("tray status 'open' is invalid for block type volume") : - _("tray status 'open' is invalid for block type disk")); - goto error; +static char * +qemuBuildNetworkDriveURI(virStorageSourcePtr src, + const char *username, + const char *secret) +{ + char *ret = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + virURIPtr uri = NULL; + size_t i; + + switch ((virStorageNetProtocol) src->protocol) { + case VIR_STORAGE_NET_PROTOCOL_NBD: + if (src->nhosts != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("protocol '%s' accepts only one host"), + virStorageNetProtocolTypeToString(src->protocol)); + goto cleanup; } - break; + if (!((src->hosts->name && strchr(src->hosts->name, ':')) || + (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP && + !src->hosts->name) || + (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX && + src->hosts->socket && + src->hosts->socket[0] != '/'))) { - default: - break; - } + virBufferAddLit(&buf, "nbd:"); - virBufferEscape(&opt, ',', ",", "%s,", source); + switch (src->hosts->transport) { + case VIR_STORAGE_NET_HOST_TRANS_TCP: + virBufferStrcat(&buf, src->hosts->name, NULL); + virBufferAsprintf(&buf, ":%s", + src->hosts->port ? src->hosts->port : + QEMU_DEFAULT_NBD_PORT); + break; - if (disk->src->format > 0 && - disk->src->type != VIR_STORAGE_TYPE_DIR) - virBufferAsprintf(&opt, "format=%s,", - virStorageFileFormatTypeToString(disk->src->format)); - } - VIR_FREE(source); + case VIR_STORAGE_NET_HOST_TRANS_UNIX: + if (!src->hosts->socket) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("socket attribute required for " + "unix transport")); + goto cleanup; + } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) - virBufferAddLit(&opt, "if=none"); - else - virBufferAsprintf(&opt, "if=%s", bus); + virBufferAsprintf(&buf, "unix:%s", src->hosts->socket); + break; - if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { - if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD)) - virBufferAddLit(&opt, ",media=cdrom"); - } else if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) - virBufferAddLit(&opt, ",media=cdrom"); - } else { - virBufferAddLit(&opt, ",media=cdrom"); - } - } + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("nbd does not support transport '%s'"), + virStorageNetHostTransportTypeToString(src->hosts->transport)); + goto cleanup; + } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { - virBufferAsprintf(&opt, ",id=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); - } else { - if (busid == -1 && unitid == -1) { - if (idx != -1) - virBufferAsprintf(&opt, ",index=%d", idx); - } else { - if (busid != -1) - virBufferAsprintf(&opt, ",bus=%d", busid); - if (unitid != -1) - virBufferAsprintf(&opt, ",unit=%d", unitid); - } - } - if (bootable && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_BOOT) && - (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK || - disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) && - disk->bus != VIR_DOMAIN_DISK_BUS_IDE) - virBufferAddLit(&opt, ",boot=on"); - if (disk->src->readonly && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("readonly ide disks are not supported")); - goto error; - } - if (disk->bus == VIR_DOMAIN_DISK_BUS_SATA) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("readonly sata disks are not supported")); - goto error; + if (src->path) + virBufferAsprintf(&buf, ":exportname=%s", src->path); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + ret = virBufferContentAndReset(&buf); + goto cleanup; } - } - virBufferAddLit(&opt, ",readonly=on"); - } - if (disk->transient) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("transient disks not supported yet")); - goto error; - } + /* fallthrough */ + /* NBD code uses same formatting scheme as others in some cases */ - /* generate geometry command string */ - if (disk->geometry.cylinders > 0 && - disk->geometry.heads > 0 && - disk->geometry.sectors > 0) { + case VIR_STORAGE_NET_PROTOCOL_HTTP: + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + case VIR_STORAGE_NET_PROTOCOL_FTP: + case VIR_STORAGE_NET_PROTOCOL_FTPS: + case VIR_STORAGE_NET_PROTOCOL_TFTP: + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + if (src->nhosts != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("protocol '%s' accepts only one host"), + virStorageNetProtocolTypeToString(src->protocol)); + goto cleanup; + } - virBufferAsprintf(&opt, ",cyls=%u,heads=%u,secs=%u", - disk->geometry.cylinders, - disk->geometry.heads, - disk->geometry.sectors); + if (VIR_ALLOC(uri) < 0) + goto cleanup; - if (disk->geometry.trans != VIR_DOMAIN_DISK_TRANS_DEFAULT) - virBufferAsprintf(&opt, ",trans=%s", trans); - } + if (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP) { + if (VIR_STRDUP(uri->scheme, + virStorageNetProtocolTypeToString(src->protocol)) < 0) + goto cleanup; + } else { + if (virAsprintf(&uri->scheme, "%s+%s", + virStorageNetProtocolTypeToString(src->protocol), + virStorageNetHostTransportTypeToString(src->hosts->transport)) < 0) + goto cleanup; + } - if (disk->serial && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_SERIAL)) { - if (qemuSafeSerialParamValue(disk->serial) < 0) - goto error; - if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI && - disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("scsi-block 'lun' devices do not support the " - "serial property")); - goto error; - } - virBufferAddLit(&opt, ",serial="); - virBufferEscape(&opt, '\\', " ", "%s", disk->serial); - } + if ((uri->port = qemuNetworkDriveGetPort(src->protocol, src->hosts->port)) < 0) + goto cleanup; - if (disk->cachemode) { - const char *mode = NULL; + if (src->path) { + if (src->volume) { + if (virAsprintf(&uri->path, "/%s%s", + src->volume, src->path) < 0) + goto cleanup; + } else { + if (virAsprintf(&uri->path, "%s%s", + src->path[0] == '/' ? "" : "/", + src->path) < 0) + goto cleanup; + } + } - mode = qemuDiskCacheV2TypeToString(disk->cachemode); + if (src->hosts->socket && + virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0) + goto cleanup; - if (disk->cachemode == VIR_DOMAIN_DISK_CACHE_DIRECTSYNC && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_CACHE_DIRECTSYNC)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disk cache mode 'directsync' is not " - "supported by this QEMU")); - goto error; - } else if (disk->cachemode == VIR_DOMAIN_DISK_CACHE_UNSAFE && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_CACHE_UNSAFE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disk cache mode 'unsafe' is not " - "supported by this QEMU")); - goto error; - } + if (username) { + if (secret) { + if (virAsprintf(&uri->user, "%s:%s", username, secret) < 0) + goto cleanup; + } else { + if (VIR_STRDUP(uri->user, username) < 0) + goto cleanup; + } + } - if (disk->iomode == VIR_DOMAIN_DISK_IO_NATIVE && - disk->cachemode != VIR_DOMAIN_DISK_CACHE_DIRECTSYNC && - disk->cachemode != VIR_DOMAIN_DISK_CACHE_DISABLE) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("native I/O needs either no disk cache " - "or directsync cache mode, QEMU will fallback " - "to aio=threads")); - goto error; - } + if (VIR_STRDUP(uri->server, src->hosts->name) < 0) + goto cleanup; - virBufferAsprintf(&opt, ",cache=%s", mode); - } else if (disk->src->shared && !disk->src->readonly) { - virBufferAddLit(&opt, ",cache=none"); - } + ret = virURIFormat(uri); - if (disk->copy_on_read) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_COPY_ON_READ)) { - virBufferAsprintf(&opt, ",copy-on-read=%s", - virTristateSwitchTypeToString(disk->copy_on_read)); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("copy_on_read is not supported by this QEMU binary")); - goto error; - } - } + break; - if (disk->discard) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_DISCARD)) { - virBufferAsprintf(&opt, ",discard=%s", - virDomainDiskDiscardTypeToString(disk->discard)); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("discard is not supported by this QEMU binary")); - goto error; - } - } + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + if (!src->path) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing disk source for 'sheepdog' protocol")); + goto cleanup; + } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MONITOR_JSON)) { - const char *wpolicy = NULL, *rpolicy = NULL; + if (src->nhosts == 0) { + if (virAsprintf(&ret, "sheepdog:%s", src->path) < 0) + goto cleanup; + } else if (src->nhosts == 1) { + if (virAsprintf(&ret, "sheepdog:%s:%s:%s", + src->hosts->name, + src->hosts->port ? src->hosts->port : "7000", + src->path) < 0) + goto cleanup; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("protocol 'sheepdog' accepts up to one host")); + goto cleanup; + } - if (disk->error_policy) - wpolicy = virDomainDiskErrorPolicyTypeToString(disk->error_policy); - if (disk->rerror_policy) - rpolicy = virDomainDiskErrorPolicyTypeToString(disk->rerror_policy); + break; - if (disk->error_policy == VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE) { - /* in the case of enospace, the option is spelled - * differently in qemu, and it's only valid for werror, - * not for rerror, so leave leave rerror NULL. - */ - wpolicy = "enospc"; - } else if (!rpolicy) { - /* for other policies, rpolicy can match wpolicy */ - rpolicy = wpolicy; - } + case VIR_STORAGE_NET_PROTOCOL_RBD: + if (strchr(src->path, ':')) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("':' not allowed in RBD source volume name '%s'"), + src->path); + goto cleanup; + } - if (wpolicy) - virBufferAsprintf(&opt, ",werror=%s", wpolicy); - if (rpolicy) - virBufferAsprintf(&opt, ",rerror=%s", rpolicy); - } + virBufferStrcat(&buf, "rbd:", src->path, NULL); - if (disk->iomode) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_AIO)) { - virBufferAsprintf(&opt, ",aio=%s", - virDomainDiskIoTypeToString(disk->iomode)); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disk aio mode not supported with this " - "QEMU binary")); - goto error; - } - } + if (src->snapshot) + virBufferEscape(&buf, '\\', ":", "@%s", src->snapshot); - /* block I/O throttling */ - if ((disk->blkdeviotune.total_bytes_sec || - disk->blkdeviotune.read_bytes_sec || - disk->blkdeviotune.write_bytes_sec || - disk->blkdeviotune.total_iops_sec || - disk->blkdeviotune.read_iops_sec || - disk->blkdeviotune.write_iops_sec) && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_IOTUNE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("block I/O throttling not supported with this " - "QEMU binary")); - goto error; - } + if (username) { + virBufferEscape(&buf, '\\', ":", ":id=%s", username); + virBufferEscape(&buf, '\\', ":", + ":key=%s:auth_supported=cephx\\;none", + secret); + } else { + virBufferAddLit(&buf, ":auth_supported=none"); + } - /* block I/O throttling 1.7 */ - if ((disk->blkdeviotune.total_bytes_sec_max || - disk->blkdeviotune.read_bytes_sec_max || - disk->blkdeviotune.write_bytes_sec_max || - disk->blkdeviotune.total_iops_sec_max || - disk->blkdeviotune.read_iops_sec_max || - disk->blkdeviotune.write_iops_sec_max || - disk->blkdeviotune.size_iops_sec) && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_IOTUNE_MAX)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("there are some block I/O throttling parameters " - "that are not supported with this QEMU binary")); - goto error; - } + if (src->nhosts > 0) { + virBufferAddLit(&buf, ":mon_host="); + for (i = 0; i < src->nhosts; i++) { + if (i) + virBufferAddLit(&buf, "\\;"); - if (disk->blkdeviotune.total_bytes_sec > LLONG_MAX || - disk->blkdeviotune.read_bytes_sec > LLONG_MAX || - disk->blkdeviotune.write_bytes_sec > LLONG_MAX || - disk->blkdeviotune.total_iops_sec > LLONG_MAX || - disk->blkdeviotune.read_iops_sec > LLONG_MAX || - disk->blkdeviotune.write_iops_sec > LLONG_MAX || - disk->blkdeviotune.total_bytes_sec_max > LLONG_MAX || - disk->blkdeviotune.read_bytes_sec_max > LLONG_MAX || - disk->blkdeviotune.write_bytes_sec_max > LLONG_MAX || - disk->blkdeviotune.total_iops_sec_max > LLONG_MAX || - disk->blkdeviotune.read_iops_sec_max > LLONG_MAX || - disk->blkdeviotune.write_iops_sec_max > LLONG_MAX || - disk->blkdeviotune.size_iops_sec > LLONG_MAX) { - virReportError(VIR_ERR_OVERFLOW, - _("block I/O throttle limit must " - "be less than %llu using QEMU"), LLONG_MAX); - goto error; - } + /* assume host containing : is ipv6 */ + if (strchr(src->hosts[i].name, ':')) + virBufferEscape(&buf, '\\', ":", "[%s]", + src->hosts[i].name); + else + virBufferAsprintf(&buf, "%s", src->hosts[i].name); - if (disk->blkdeviotune.total_bytes_sec) { - virBufferAsprintf(&opt, ",bps=%llu", - disk->blkdeviotune.total_bytes_sec); - } + if (src->hosts[i].port) + virBufferAsprintf(&buf, "\\:%s", src->hosts[i].port); + } + } - if (disk->blkdeviotune.read_bytes_sec) { - virBufferAsprintf(&opt, ",bps_rd=%llu", - disk->blkdeviotune.read_bytes_sec); - } + if (src->configFile) + virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile); - if (disk->blkdeviotune.write_bytes_sec) { - virBufferAsprintf(&opt, ",bps_wr=%llu", - disk->blkdeviotune.write_bytes_sec); - } + if (virBufferCheckError(&buf) < 0) + goto cleanup; - if (disk->blkdeviotune.total_iops_sec) { - virBufferAsprintf(&opt, ",iops=%llu", - disk->blkdeviotune.total_iops_sec); - } + ret = virBufferContentAndReset(&buf); + break; - if (disk->blkdeviotune.read_iops_sec) { - virBufferAsprintf(&opt, ",iops_rd=%llu", - disk->blkdeviotune.read_iops_sec); - } - if (disk->blkdeviotune.write_iops_sec) { - virBufferAsprintf(&opt, ",iops_wr=%llu", - disk->blkdeviotune.write_iops_sec); + case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected network protocol '%s'"), + virStorageNetProtocolTypeToString(src->protocol)); + goto cleanup; } - if (disk->blkdeviotune.total_bytes_sec_max) { - virBufferAsprintf(&opt, ",bps_max=%llu", - disk->blkdeviotune.total_bytes_sec_max); - } + cleanup: + virBufferFreeAndReset(&buf); + virURIFree(uri); - if (disk->blkdeviotune.read_bytes_sec_max) { - virBufferAsprintf(&opt, ",bps_rd_max=%llu", - disk->blkdeviotune.read_bytes_sec_max); - } + return ret; +} - if (disk->blkdeviotune.write_bytes_sec_max) { - virBufferAsprintf(&opt, ",bps_wr_max=%llu", - disk->blkdeviotune.write_bytes_sec_max); - } - if (disk->blkdeviotune.total_iops_sec_max) { - virBufferAsprintf(&opt, ",iops_max=%llu", - disk->blkdeviotune.total_iops_sec_max); - } +int +qemuGetDriveSourceString(virStorageSourcePtr src, + virConnectPtr conn, + char **source) +{ + int actualType = virStorageSourceGetActualType(src); + char *secret = NULL; + char *username = NULL; + int ret = -1; - if (disk->blkdeviotune.read_iops_sec_max) { - virBufferAsprintf(&opt, ",iops_rd_max=%llu", - disk->blkdeviotune.read_iops_sec_max); - } + *source = NULL; - if (disk->blkdeviotune.write_iops_sec_max) { - virBufferAsprintf(&opt, ",iops_wr_max=%llu", - disk->blkdeviotune.write_iops_sec_max); - } + /* return 1 for empty sources */ + if (virStorageSourceIsEmpty(src)) + return 1; - if (disk->blkdeviotune.size_iops_sec) { - virBufferAsprintf(&opt, ",iops_size=%llu", - disk->blkdeviotune.size_iops_sec); - } + if (conn) { + if (actualType == VIR_STORAGE_TYPE_NETWORK && + src->auth && + (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI || + src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { + bool encode = false; + int secretType = VIR_SECRET_USAGE_TYPE_ISCSI; + const char *protocol = virStorageNetProtocolTypeToString(src->protocol); + username = src->auth->username; - if (virBufferCheckError(&opt) < 0) - goto error; + if (src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) { + /* qemu requires the secret to be encoded for RBD */ + encode = true; + secretType = VIR_SECRET_USAGE_TYPE_CEPH; + } - return virBufferContentAndReset(&opt); + if (!(secret = qemuGetSecretString(conn, + protocol, + encode, + src->auth, + secretType))) + goto cleanup; + } + } - error: - VIR_FREE(source); - virBufferFreeAndReset(&opt); - return NULL; -} + switch ((virStorageType) actualType) { + case VIR_STORAGE_TYPE_BLOCK: + case VIR_STORAGE_TYPE_FILE: + case VIR_STORAGE_TYPE_DIR: + if (VIR_STRDUP(*source, src->path) < 0) + goto cleanup; + break; -static bool -qemuCheckIOThreads(virDomainDefPtr def, - virDomainDiskDefPtr disk) -{ - /* Right "type" of disk" */ - if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO || - (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && - disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("IOThreads only available for virtio pci and " - "virtio ccw disk")); - return false; - } + case VIR_STORAGE_TYPE_NETWORK: + if (!(*source = qemuBuildNetworkDriveURI(src, username, secret))) + goto cleanup; + break; - /* Can we find the disk iothread in the iothreadid list? */ - if (!virDomainIOThreadIDFind(def, disk->iothread)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Disk iothread '%u' not defined in iothreadid"), - disk->iothread); - return false; + case VIR_STORAGE_TYPE_VOLUME: + case VIR_STORAGE_TYPE_NONE: + case VIR_STORAGE_TYPE_LAST: + break; } - return true; + ret = 0; + + cleanup: + VIR_FREE(secret); + return ret; } -char * -qemuBuildDriveDevStr(virDomainDefPtr def, - virDomainDiskDefPtr disk, - int bootindex, - virQEMUCapsPtr qemuCaps) +/* Perform disk definition config validity checks */ +int +qemuCheckDiskConfig(virDomainDiskDefPtr disk) { - virBuffer opt = VIR_BUFFER_INITIALIZER; - const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); - const char *contAlias; - int controllerModel; - - if (qemuCheckDiskConfig(disk) < 0) + if (virDiskNameToIndex(disk->dst) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), disk->dst); goto error; + } - /* Live only checks */ - if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { - /* make sure that the qemu binary supports type='lun' (SG_IO). */ - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SG_IO)) { + if (disk->wwn) { + if ((disk->bus != VIR_DOMAIN_DISK_BUS_IDE) && + (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disk device='lun' is not supported by " - "this QEMU")); + _("Only ide and scsi disk support wwn")); goto error; } } - if (!qemuCheckCCWS390AddressSupport(def, disk->info, qemuCaps, disk->dst)) - goto error; - - if (disk->iothread && !qemuCheckIOThreads(def, disk)) - goto error; - - switch (disk->bus) { - case VIR_DOMAIN_DISK_BUS_IDE: - if (disk->info.addr.drive.target != 0) { + if ((disk->vendor || disk->product) && + disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("target must be 0 for ide controller")); + _("Only scsi disk supports vendor and product")); goto error; - } + } - if (disk->wwn && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_DRIVE_WWN)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Setting wwn for ide disk is not supported " - "by this QEMU")); + if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { + /* make sure that both the bus supports type='lun' (SG_IO). */ + if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO && + disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk device='lun' is not supported for bus='%s'"), + virDomainDiskQEMUBusTypeToString(disk->bus)); goto error; } - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) - virBufferAddLit(&opt, "ide-cd"); - else - virBufferAddLit(&opt, "ide-hd"); - } else { - virBufferAddLit(&opt, "ide-drive"); - } - - if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_IDE, - disk->info.addr.drive.controller))) - goto error; - virBufferAsprintf(&opt, ",bus=%s.%d,unit=%d", - contAlias, - disk->info.addr.drive.bus, - disk->info.addr.drive.unit); - break; - - case VIR_DOMAIN_DISK_BUS_SCSI: - if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_BLOCK)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU doesn't support scsi-block for " - "lun passthrough")); + if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) { + if (disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_ISCSI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk device='lun' is not supported " + "for protocol='%s'"), + virStorageNetProtocolTypeToString(disk->src->protocol)); goto error; } + } else if (!virDomainDiskSourceIsBlockType(disk->src, true)) { + goto error; } - - if (disk->wwn && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_WWN)) { + if (disk->wwn) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Setting wwn for scsi disk is not supported " - "by this QEMU")); + _("Setting wwn is not supported for lun device")); goto error; } - - /* Properties wwn, vendor and product were introduced in the - * same QEMU release (1.2.0). - */ - if ((disk->vendor || disk->product) && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_WWN)) { + if (disk->vendor || disk->product) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Setting vendor or product for scsi disk is not " - "supported by this QEMU")); + _("Setting vendor or product is not supported " + "for lun device")); goto error; } + } + return 0; + error: + return -1; +} - controllerModel = - virDomainDeviceFindControllerModel(def, &disk->info, - VIR_DOMAIN_CONTROLLER_TYPE_SCSI); - if ((qemuDomainSetSCSIControllerModel(def, qemuCaps, - &controllerModel)) < 0) - goto error; - if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { - virBufferAddLit(&opt, "scsi-block"); - } else { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD)) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) - virBufferAddLit(&opt, "scsi-cd"); - else - virBufferAddLit(&opt, "scsi-hd"); - } else { - virBufferAddLit(&opt, "scsi-disk"); - } +/* Check whether the device address is using either 'ccw' or default s390 + * address format and whether that's "legal" for the current qemu and/or + * guest os.machine type. This is the corollary to the code which doesn't + * find the address type set using an emulator that supports either 'ccw' + * or s390 and sets the address type based on the capabilities. + * + * If the address is using 'ccw' or s390 and it's not supported, generate + * an error and return false; otherwise, return true. + */ +bool +qemuCheckCCWS390AddressSupport(virDomainDefPtr def, + virDomainDeviceInfo info, + virQEMUCapsPtr qemuCaps, + const char *devicename) +{ + if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { + if (!qemuDomainMachineIsS390CCW(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("cannot use CCW address type for device " + "'%s' using machine type '%s'"), + devicename, def->os.machine); + return false; + } else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("CCW address type is not supported by " + "this QEMU")); + return false; + } + } else if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("virtio S390 address type is not supported by " + "this QEMU")); + return false; } + } + return true; +} - if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, - disk->info.addr.drive.controller))) - goto error; - if (controllerModel == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) { - if (disk->info.addr.drive.target != 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("target must be 0 for controller " - "model 'lsilogic'")); - goto error; - } +/* Qemu 1.2 and later have a binary flag -enable-fips that must be + * used for VNC auth to obey FIPS settings; but the flag only + * exists on Linux, and with no way to probe for it via QMP. Our + * solution: if FIPS mode is required, then unconditionally use + * the flag, regardless of qemu version, for the following matrix: + * + * old QEMU new QEMU + * FIPS enabled doesn't start VNC auth disabled + * FIPS disabled/missing VNC auth enabled VNC auth enabled + */ +bool +qemuCheckFips(void) +{ + bool ret = false; - virBufferAsprintf(&opt, ",bus=%s.%d,scsi-id=%d", - contAlias, - disk->info.addr.drive.bus, - disk->info.addr.drive.unit); - } else { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_CHANNEL)) { - if (disk->info.addr.drive.target > 7) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU doesn't support target " - "greater than 7")); - goto error; - } + if (virFileExists("/proc/sys/crypto/fips_enabled")) { + char *buf = NULL; - if (disk->info.addr.drive.bus != 0 && - disk->info.addr.drive.unit != 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU only supports both bus and " - "unit equal to 0")); - goto error; - } - } + if (virFileReadAll("/proc/sys/crypto/fips_enabled", 10, &buf) < 0) + return ret; + if (STREQ(buf, "1\n")) + ret = true; + VIR_FREE(buf); + } - virBufferAsprintf(&opt, ",bus=%s.0,channel=%d,scsi-id=%d,lun=%d", - contAlias, - disk->info.addr.drive.bus, - disk->info.addr.drive.target, - disk->info.addr.drive.unit); - } - break; + return ret; +} - case VIR_DOMAIN_DISK_BUS_SATA: - if (disk->info.addr.drive.bus != 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("bus must be 0 for ide controller")); - goto error; - } - if (disk->info.addr.drive.target != 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("target must be 0 for ide controller")); + +char * +qemuBuildDriveStr(virConnectPtr conn, + virDomainDiskDefPtr disk, + bool bootable, + virQEMUCapsPtr qemuCaps) +{ + virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); + const char *trans = + virDomainDiskGeometryTransTypeToString(disk->geometry.trans); + int idx = virDiskNameToIndex(disk->dst); + int busid = -1, unitid = -1; + char *source = NULL; + int actualType = virStorageSourceGetActualType(disk->src); + + if (idx < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), disk->dst); + goto error; + } + + switch (disk->bus) { + case VIR_DOMAIN_DISK_BUS_SCSI: + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected address type for scsi disk")); goto error; } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) - virBufferAddLit(&opt, "ide-cd"); - else - virBufferAddLit(&opt, "ide-hd"); - } else { - virBufferAddLit(&opt, "ide-drive"); + /* Setting bus= attr for SCSI drives, causes a controller + * to be created. Yes this is slightly odd. It is not possible + * to have > 1 bus on a SCSI controller (yet). */ + if (disk->info.addr.drive.bus != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("SCSI controller only supports 1 bus")); + goto error; } - - if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, - disk->info.addr.drive.controller))) - goto error; - virBufferAsprintf(&opt, ",bus=%s.%d", - contAlias, - disk->info.addr.drive.unit); + busid = disk->info.addr.drive.controller; + unitid = disk->info.addr.drive.unit; break; - case VIR_DOMAIN_DISK_BUS_VIRTIO: - if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { - virBufferAddLit(&opt, "virtio-blk-ccw"); - if (disk->iothread) - virBufferAsprintf(&opt, ",iothread=iothread%u", disk->iothread); - } else if (disk->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { - virBufferAddLit(&opt, "virtio-blk-s390"); - } else if (disk->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) { - virBufferAddLit(&opt, "virtio-blk-device"); - } else { - virBufferAddLit(&opt, "virtio-blk-pci"); - if (disk->iothread) - virBufferAsprintf(&opt, ",iothread=iothread%u", disk->iothread); - } - qemuBuildIoEventFdStr(&opt, disk->ioeventfd, qemuCaps); - if (disk->event_idx && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_EVENT_IDX)) { - virBufferAsprintf(&opt, ",event_idx=%s", - virTristateSwitchTypeToString(disk->event_idx)); - } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SCSI)) { - /* if sg_io is true but the scsi option isn't supported, - * that means it's just always on in this version of qemu. - */ - virBufferAsprintf(&opt, ",scsi=%s", - (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) - ? "on" : "off"); + case VIR_DOMAIN_DISK_BUS_IDE: + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected address type for ide disk")); + goto error; } - if (qemuBuildDeviceAddressStr(&opt, def, &disk->info, qemuCaps) < 0) + /* We can only have 1 IDE controller (currently) */ + if (disk->info.addr.drive.controller != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Only 1 %s controller is supported"), bus); goto error; + } + busid = disk->info.addr.drive.bus; + unitid = disk->info.addr.drive.unit; break; - case VIR_DOMAIN_DISK_BUS_USB: - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && - disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { + case VIR_DOMAIN_DISK_BUS_FDC: + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected address type for usb disk")); + _("unexpected address type for fdc disk")); goto error; } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) { + /* We can only have 1 FDC controller (currently) */ + if (disk->info.addr.drive.controller != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Only 1 %s controller is supported"), bus); + goto error; + } + /* We can only have 1 FDC bus (currently) */ + if (disk->info.addr.drive.bus != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Only 1 %s bus is supported"), bus); + goto error; + } + if (disk->info.addr.drive.target != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU doesn't support '-device " - "usb-storage'")); + _("target must be 0 for controller fdc")); goto error; - } - virBufferAddLit(&opt, "usb-storage"); + unitid = disk->info.addr.drive.unit; - if (qemuBuildDeviceAddressStr(&opt, def, &disk->info, qemuCaps) < 0) - goto error; break; - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk bus '%s' with device setup"), bus); - goto error; - } + case VIR_DOMAIN_DISK_BUS_VIRTIO: + idx = -1; + break; - virBufferAsprintf(&opt, ",drive=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); - virBufferAsprintf(&opt, ",id=%s", disk->info.alias); - if (bootindex && virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX)) - virBufferAsprintf(&opt, ",bootindex=%d", bootindex); - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKIO)) { - if (disk->blockio.logical_block_size > 0) - virBufferAsprintf(&opt, ",logical_block_size=%u", - disk->blockio.logical_block_size); - if (disk->blockio.physical_block_size > 0) - virBufferAsprintf(&opt, ",physical_block_size=%u", - disk->blockio.physical_block_size); + case VIR_DOMAIN_DISK_BUS_XEN: + case VIR_DOMAIN_DISK_BUS_SD: + /* Xen and SD have no address type currently, so assign + * based on index */ + break; } - if (disk->wwn) { - if (STRPREFIX(disk->wwn, "0x")) - virBufferAsprintf(&opt, ",wwn=%s", disk->wwn); - else - virBufferAsprintf(&opt, ",wwn=0x%s", disk->wwn); - } + if (qemuGetDriveSourceString(disk->src, conn, &source) < 0) + goto error; - if (disk->vendor) - virBufferAsprintf(&opt, ",vendor=%s", disk->vendor); + if (source && + !((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || + disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) && + disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { - if (disk->product) - virBufferAsprintf(&opt, ",product=%s", disk->product); + virBufferAddLit(&opt, "file="); - if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_STORAGE_REMOVABLE)) { - if (disk->removable == VIR_TRISTATE_SWITCH_ON) - virBufferAddLit(&opt, ",removable=on"); - else - virBufferAddLit(&opt, ",removable=off"); - } else { - if (disk->removable != VIR_TRISTATE_SWITCH_ABSENT) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU doesn't support setting the " - "removable flag of USB storage devices")); + switch (actualType) { + case VIR_STORAGE_TYPE_DIR: + /* QEMU only supports magic FAT format for now */ + if (disk->src->format > 0 && + disk->src->format != VIR_STORAGE_FILE_FAT) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk driver type for '%s'"), + virStorageFileFormatTypeToString(disk->src->format)); goto error; } - } - } - if (virBufferCheckError(&opt) < 0) - goto error; + if (!disk->src->readonly) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create virtual FAT disks in read-write mode")); + goto error; + } - return virBufferContentAndReset(&opt); + virBufferAddLit(&opt, "fat:"); - error: - virBufferFreeAndReset(&opt); - return NULL; -} + if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) + virBufferAddLit(&opt, "floppy:"); + break; -char *qemuBuildFSStr(virDomainFSDefPtr fs, - virQEMUCapsPtr qemuCaps ATTRIBUTE_UNUSED) -{ - virBuffer opt = VIR_BUFFER_INITIALIZER; - const char *driver = qemuDomainFSDriverTypeToString(fs->fsdriver); - const char *wrpolicy = virDomainFSWrpolicyTypeToString(fs->wrpolicy); + case VIR_STORAGE_TYPE_BLOCK: + if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + disk->src->type == VIR_STORAGE_TYPE_VOLUME ? + _("tray status 'open' is invalid for block type volume") : + _("tray status 'open' is invalid for block type disk")); + goto error; + } - if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("only supports mount filesystem type")); - goto error; + break; + + default: + break; + } + + virBufferEscape(&opt, ',', ",", "%s,", source); + + if (disk->src->format > 0 && + disk->src->type != VIR_STORAGE_TYPE_DIR) + virBufferAsprintf(&opt, "format=%s,", + virStorageFileFormatTypeToString(disk->src->format)); } + VIR_FREE(source); - if (!driver) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) + virBufferAddLit(&opt, "if=none"); + else + virBufferAsprintf(&opt, "if=%s", bus); + + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD)) + virBufferAddLit(&opt, ",media=cdrom"); + } else if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) + virBufferAddLit(&opt, ",media=cdrom"); + } else { + virBufferAddLit(&opt, ",media=cdrom"); + } + } + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { + virBufferAsprintf(&opt, ",id=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); + } else { + if (busid == -1 && unitid == -1) { + if (idx != -1) + virBufferAsprintf(&opt, ",index=%d", idx); + } else { + if (busid != -1) + virBufferAsprintf(&opt, ",bus=%d", busid); + if (unitid != -1) + virBufferAsprintf(&opt, ",unit=%d", unitid); + } + } + if (bootable && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_BOOT) && + (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK || + disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) && + disk->bus != VIR_DOMAIN_DISK_BUS_IDE) + virBufferAddLit(&opt, ",boot=on"); + if (disk->src->readonly && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("readonly ide disks are not supported")); + goto error; + } + if (disk->bus == VIR_DOMAIN_DISK_BUS_SATA) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("readonly sata disks are not supported")); + goto error; + } + } + virBufferAddLit(&opt, ",readonly=on"); + } + if (disk->transient) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Filesystem driver type not supported")); + _("transient disks not supported yet")); goto error; } - virBufferAdd(&opt, driver, -1); - if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PATH || - fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT) { - if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_MAPPED) { - virBufferAddLit(&opt, ",security_model=mapped"); - } else if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) { - virBufferAddLit(&opt, ",security_model=passthrough"); - } else if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_SQUASH) { - virBufferAddLit(&opt, ",security_model=none"); + /* generate geometry command string */ + if (disk->geometry.cylinders > 0 && + disk->geometry.heads > 0 && + disk->geometry.sectors > 0) { + + virBufferAsprintf(&opt, ",cyls=%u,heads=%u,secs=%u", + disk->geometry.cylinders, + disk->geometry.heads, + disk->geometry.sectors); + + if (disk->geometry.trans != VIR_DOMAIN_DISK_TRANS_DEFAULT) + virBufferAsprintf(&opt, ",trans=%s", trans); + } + + if (disk->serial && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_SERIAL)) { + if (qemuSafeSerialParamValue(disk->serial) < 0) + goto error; + if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI && + disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("scsi-block 'lun' devices do not support the " + "serial property")); + goto error; } - } else { - /* For other fs drivers, default(passthru) should always - * be supported */ - if (fs->accessmode != VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) { + virBufferAddLit(&opt, ",serial="); + virBufferEscape(&opt, '\\', " ", "%s", disk->serial); + } + + if (disk->cachemode) { + const char *mode = NULL; + + mode = qemuDiskCacheV2TypeToString(disk->cachemode); + + if (disk->cachemode == VIR_DOMAIN_DISK_CACHE_DIRECTSYNC && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_CACHE_DIRECTSYNC)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("only supports passthrough accessmode")); + _("disk cache mode 'directsync' is not " + "supported by this QEMU")); + goto error; + } else if (disk->cachemode == VIR_DOMAIN_DISK_CACHE_UNSAFE && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_CACHE_UNSAFE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("disk cache mode 'unsafe' is not " + "supported by this QEMU")); + goto error; + } + + if (disk->iomode == VIR_DOMAIN_DISK_IO_NATIVE && + disk->cachemode != VIR_DOMAIN_DISK_CACHE_DIRECTSYNC && + disk->cachemode != VIR_DOMAIN_DISK_CACHE_DISABLE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("native I/O needs either no disk cache " + "or directsync cache mode, QEMU will fallback " + "to aio=threads")); goto error; } + + virBufferAsprintf(&opt, ",cache=%s", mode); + } else if (disk->src->shared && !disk->src->readonly) { + virBufferAddLit(&opt, ",cache=none"); } - if (fs->wrpolicy) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_WRITEOUT)) { - virBufferAsprintf(&opt, ",writeout=%s", wrpolicy); + if (disk->copy_on_read) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_COPY_ON_READ)) { + virBufferAsprintf(&opt, ",copy-on-read=%s", + virTristateSwitchTypeToString(disk->copy_on_read)); } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("filesystem writeout not supported")); + _("copy_on_read is not supported by this QEMU binary")); goto error; } } - virBufferAsprintf(&opt, ",id=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); - virBufferAsprintf(&opt, ",path=%s", fs->src); - - if (fs->readonly) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_READONLY)) { - virBufferAddLit(&opt, ",readonly"); + if (disk->discard) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_DISCARD)) { + virBufferAsprintf(&opt, ",discard=%s", + virDomainDiskDiscardTypeToString(disk->discard)); } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("readonly filesystem is not supported by this " - "QEMU binary")); + _("discard is not supported by this QEMU binary")); goto error; } } - if (virBufferCheckError(&opt) < 0) - goto error; + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MONITOR_JSON)) { + const char *wpolicy = NULL, *rpolicy = NULL; - return virBufferContentAndReset(&opt); + if (disk->error_policy) + wpolicy = virDomainDiskErrorPolicyTypeToString(disk->error_policy); + if (disk->rerror_policy) + rpolicy = virDomainDiskErrorPolicyTypeToString(disk->rerror_policy); - error: - virBufferFreeAndReset(&opt); - return NULL; -} + if (disk->error_policy == VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE) { + /* in the case of enospace, the option is spelled + * differently in qemu, and it's only valid for werror, + * not for rerror, so leave leave rerror NULL. + */ + wpolicy = "enospc"; + } else if (!rpolicy) { + /* for other policies, rpolicy can match wpolicy */ + rpolicy = wpolicy; + } + if (wpolicy) + virBufferAsprintf(&opt, ",werror=%s", wpolicy); + if (rpolicy) + virBufferAsprintf(&opt, ",rerror=%s", rpolicy); + } -char * -qemuBuildFSDevStr(virDomainDefPtr def, - virDomainFSDefPtr fs, - virQEMUCapsPtr qemuCaps) -{ - virBuffer opt = VIR_BUFFER_INITIALIZER; + if (disk->iomode) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_AIO)) { + virBufferAsprintf(&opt, ",aio=%s", + virDomainDiskIoTypeToString(disk->iomode)); + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("disk aio mode not supported with this " + "QEMU binary")); + goto error; + } + } - if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { + /* block I/O throttling */ + if ((disk->blkdeviotune.total_bytes_sec || + disk->blkdeviotune.read_bytes_sec || + disk->blkdeviotune.write_bytes_sec || + disk->blkdeviotune.total_iops_sec || + disk->blkdeviotune.read_iops_sec || + disk->blkdeviotune.write_iops_sec) && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_IOTUNE)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("can only passthrough directories")); + _("block I/O throttling not supported with this " + "QEMU binary")); goto error; } - if (fs->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) - virBufferAddLit(&opt, "virtio-9p-ccw"); - else - virBufferAddLit(&opt, "virtio-9p-pci"); - - virBufferAsprintf(&opt, ",id=%s", fs->info.alias); - virBufferAsprintf(&opt, ",fsdev=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); - virBufferAsprintf(&opt, ",mount_tag=%s", fs->dst); - - if (qemuBuildDeviceAddressStr(&opt, def, &fs->info, qemuCaps) < 0) - goto error; - - if (virBufferCheckError(&opt) < 0) - goto error; - - return virBufferContentAndReset(&opt); + /* block I/O throttling 1.7 */ + if ((disk->blkdeviotune.total_bytes_sec_max || + disk->blkdeviotune.read_bytes_sec_max || + disk->blkdeviotune.write_bytes_sec_max || + disk->blkdeviotune.total_iops_sec_max || + disk->blkdeviotune.read_iops_sec_max || + disk->blkdeviotune.write_iops_sec_max || + disk->blkdeviotune.size_iops_sec) && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_IOTUNE_MAX)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("there are some block I/O throttling parameters " + "that are not supported with this QEMU binary")); + goto error; + } - error: - virBufferFreeAndReset(&opt); - return NULL; -} + if (disk->blkdeviotune.total_bytes_sec > LLONG_MAX || + disk->blkdeviotune.read_bytes_sec > LLONG_MAX || + disk->blkdeviotune.write_bytes_sec > LLONG_MAX || + disk->blkdeviotune.total_iops_sec > LLONG_MAX || + disk->blkdeviotune.read_iops_sec > LLONG_MAX || + disk->blkdeviotune.write_iops_sec > LLONG_MAX || + disk->blkdeviotune.total_bytes_sec_max > LLONG_MAX || + disk->blkdeviotune.read_bytes_sec_max > LLONG_MAX || + disk->blkdeviotune.write_bytes_sec_max > LLONG_MAX || + disk->blkdeviotune.total_iops_sec_max > LLONG_MAX || + disk->blkdeviotune.read_iops_sec_max > LLONG_MAX || + disk->blkdeviotune.write_iops_sec_max > LLONG_MAX || + disk->blkdeviotune.size_iops_sec > LLONG_MAX) { + virReportError(VIR_ERR_OVERFLOW, + _("block I/O throttle limit must " + "be less than %llu using QEMU"), LLONG_MAX); + goto error; + } + if (disk->blkdeviotune.total_bytes_sec) { + virBufferAsprintf(&opt, ",bps=%llu", + disk->blkdeviotune.total_bytes_sec); + } -static int -qemuControllerModelUSBToCaps(int model) -{ - switch (model) { - case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI: - return QEMU_CAPS_PIIX3_USB_UHCI; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI: - return QEMU_CAPS_PIIX4_USB_UHCI; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_EHCI: - return QEMU_CAPS_USB_EHCI; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1: - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1: - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2: - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3: - return QEMU_CAPS_ICH9_USB_EHCI1; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI: - return QEMU_CAPS_VT82C686B_USB_UHCI; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI: - return QEMU_CAPS_PCI_OHCI; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI: - return QEMU_CAPS_NEC_USB_XHCI; - default: - return -1; + if (disk->blkdeviotune.read_bytes_sec) { + virBufferAsprintf(&opt, ",bps_rd=%llu", + disk->blkdeviotune.read_bytes_sec); } -} + if (disk->blkdeviotune.write_bytes_sec) { + virBufferAsprintf(&opt, ",bps_wr=%llu", + disk->blkdeviotune.write_bytes_sec); + } -static int -qemuBuildUSBControllerDevStr(virDomainDefPtr domainDef, - virDomainControllerDefPtr def, - virQEMUCapsPtr qemuCaps, - virBuffer *buf) -{ - const char *smodel; - int model, flags; + if (disk->blkdeviotune.total_iops_sec) { + virBufferAsprintf(&opt, ",iops=%llu", + disk->blkdeviotune.total_iops_sec); + } - model = def->model; + if (disk->blkdeviotune.read_iops_sec) { + virBufferAsprintf(&opt, ",iops_rd=%llu", + disk->blkdeviotune.read_iops_sec); + } - if (model == -1) { - if ARCH_IS_PPC64(domainDef->os.arch) - model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI; - else - model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI; + if (disk->blkdeviotune.write_iops_sec) { + virBufferAsprintf(&opt, ",iops_wr=%llu", + disk->blkdeviotune.write_iops_sec); } - smodel = qemuControllerModelUSBTypeToString(model); - flags = qemuControllerModelUSBToCaps(model); + if (disk->blkdeviotune.total_bytes_sec_max) { + virBufferAsprintf(&opt, ",bps_max=%llu", + disk->blkdeviotune.total_bytes_sec_max); + } - if (flags == -1 || !virQEMUCapsGet(qemuCaps, flags)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("%s not supported in this QEMU binary"), smodel); - return -1; + if (disk->blkdeviotune.read_bytes_sec_max) { + virBufferAsprintf(&opt, ",bps_rd_max=%llu", + disk->blkdeviotune.read_bytes_sec_max); } - virBufferAsprintf(buf, "%s", smodel); + if (disk->blkdeviotune.write_bytes_sec_max) { + virBufferAsprintf(&opt, ",bps_wr_max=%llu", + disk->blkdeviotune.write_bytes_sec_max); + } - if (def->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_USB) - virBufferAsprintf(buf, ",masterbus=%s.0,firstport=%d", - def->info.alias, def->info.master.usb.startport); - else - virBufferAsprintf(buf, ",id=%s", def->info.alias); + if (disk->blkdeviotune.total_iops_sec_max) { + virBufferAsprintf(&opt, ",iops_max=%llu", + disk->blkdeviotune.total_iops_sec_max); + } - return 0; + if (disk->blkdeviotune.read_iops_sec_max) { + virBufferAsprintf(&opt, ",iops_rd_max=%llu", + disk->blkdeviotune.read_iops_sec_max); + } + + if (disk->blkdeviotune.write_iops_sec_max) { + virBufferAsprintf(&opt, ",iops_wr_max=%llu", + disk->blkdeviotune.write_iops_sec_max); + } + + if (disk->blkdeviotune.size_iops_sec) { + virBufferAsprintf(&opt, ",iops_size=%llu", + disk->blkdeviotune.size_iops_sec); + } + + if (virBufferCheckError(&opt) < 0) + goto error; + + return virBufferContentAndReset(&opt); + + error: + VIR_FREE(source); + virBufferFreeAndReset(&opt); + return NULL; } -char * -qemuBuildControllerDevStr(virDomainDefPtr domainDef, - virDomainControllerDefPtr def, - virQEMUCapsPtr qemuCaps, - int *nusbcontroller) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - int model = def->model; - const char *modelName = NULL; - if (!qemuCheckCCWS390AddressSupport(domainDef, def->info, qemuCaps, - "controller")) - return NULL; +static bool +qemuCheckIOThreads(virDomainDefPtr def, + virDomainDiskDefPtr disk) +{ + /* Right "type" of disk" */ + if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO || + (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && + disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("IOThreads only available for virtio pci and " + "virtio ccw disk")); + return false; + } - if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { - if ((qemuDomainSetSCSIControllerModel(domainDef, qemuCaps, &model)) < 0) - return NULL; + /* Can we find the disk iothread in the iothreadid list? */ + if (!virDomainIOThreadIDFind(def, disk->iothread)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Disk iothread '%u' not defined in iothreadid"), + disk->iothread); + return false; } - if (!(def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI && - model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI)) { - if (def->queues) { + return true; +} + + +char * +qemuBuildDriveDevStr(virDomainDefPtr def, + virDomainDiskDefPtr disk, + int bootindex, + virQEMUCapsPtr qemuCaps) +{ + virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); + const char *contAlias; + int controllerModel; + + if (qemuCheckDiskConfig(disk) < 0) + goto error; + + /* Live only checks */ + if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { + /* make sure that the qemu binary supports type='lun' (SG_IO). */ + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SG_IO)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("'queues' is only supported by virtio-scsi controller")); - return NULL; + _("disk device='lun' is not supported by " + "this QEMU")); + goto error; } - if (def->cmd_per_lun) { + } + + if (!qemuCheckCCWS390AddressSupport(def, disk->info, qemuCaps, disk->dst)) + goto error; + + if (disk->iothread && !qemuCheckIOThreads(def, disk)) + goto error; + + switch (disk->bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + if (disk->info.addr.drive.target != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("'cmd_per_lun' is only supported by virtio-scsi controller")); - return NULL; + _("target must be 0 for ide controller")); + goto error; } - if (def->max_sectors) { + + if (disk->wwn && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_DRIVE_WWN)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("'max_sectors' is only supported by virtio-scsi controller")); - return NULL; + _("Setting wwn for ide disk is not supported " + "by this QEMU")); + goto error; } - if (def->ioeventfd) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("'ioeventfd' is only supported by virtio-scsi controller")); - return NULL; + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) + virBufferAddLit(&opt, "ide-cd"); + else + virBufferAddLit(&opt, "ide-hd"); + } else { + virBufferAddLit(&opt, "ide-drive"); } - } - switch (def->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - switch (model) { - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI: - if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) - virBufferAddLit(&buf, "virtio-scsi-ccw"); - else if (def->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) - virBufferAddLit(&buf, "virtio-scsi-s390"); - else if (def->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) - virBufferAddLit(&buf, "virtio-scsi-device"); - else - virBufferAddLit(&buf, "virtio-scsi-pci"); - break; - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC: - virBufferAddLit(&buf, "lsi"); - break; - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI: - virBufferAddLit(&buf, "spapr-vscsi"); - break; - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078: - virBufferAddLit(&buf, "megasas"); - break; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported controller model: %s"), - virDomainControllerModelSCSITypeToString(def->model)); - } - virBufferAsprintf(&buf, ",id=%s", def->info.alias); + if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_IDE, + disk->info.addr.drive.controller))) + goto error; + virBufferAsprintf(&opt, ",bus=%s.%d,unit=%d", + contAlias, + disk->info.addr.drive.bus, + disk->info.addr.drive.unit); break; - case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: - if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - virBufferAddLit(&buf, "virtio-serial-pci"); - } else if (def->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { - virBufferAddLit(&buf, "virtio-serial-ccw"); - } else if (def->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { - virBufferAddLit(&buf, "virtio-serial-s390"); - } else if (def->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) { - virBufferAddLit(&buf, "virtio-serial-device"); - } else { - virBufferAddLit(&buf, "virtio-serial"); + case VIR_DOMAIN_DISK_BUS_SCSI: + if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_BLOCK)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU doesn't support scsi-block for " + "lun passthrough")); + goto error; + } } - virBufferAsprintf(&buf, ",id=%s", def->info.alias); - if (def->opts.vioserial.ports != -1) { - virBufferAsprintf(&buf, ",max_ports=%d", - def->opts.vioserial.ports); + + if (disk->wwn && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_WWN)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Setting wwn for scsi disk is not supported " + "by this QEMU")); + goto error; } - if (def->opts.vioserial.vectors != -1) { - virBufferAsprintf(&buf, ",vectors=%d", - def->opts.vioserial.vectors); + + /* Properties wwn, vendor and product were introduced in the + * same QEMU release (1.2.0). + */ + if ((disk->vendor || disk->product) && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_WWN)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Setting vendor or product for scsi disk is not " + "supported by this QEMU")); + goto error; } - break; - case VIR_DOMAIN_CONTROLLER_TYPE_CCID: - virBufferAsprintf(&buf, "usb-ccid,id=%s", def->info.alias); + controllerModel = + virDomainDeviceFindControllerModel(def, &disk->info, + VIR_DOMAIN_CONTROLLER_TYPE_SCSI); + if ((qemuDomainSetSCSIControllerModel(def, qemuCaps, + &controllerModel)) < 0) + goto error; + + if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { + virBufferAddLit(&opt, "scsi-block"); + } else { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD)) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) + virBufferAddLit(&opt, "scsi-cd"); + else + virBufferAddLit(&opt, "scsi-hd"); + } else { + virBufferAddLit(&opt, "scsi-disk"); + } + } + + if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, + disk->info.addr.drive.controller))) + goto error; + + if (controllerModel == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) { + if (disk->info.addr.drive.target != 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("target must be 0 for controller " + "model 'lsilogic'")); + goto error; + } + + virBufferAsprintf(&opt, ",bus=%s.%d,scsi-id=%d", + contAlias, + disk->info.addr.drive.bus, + disk->info.addr.drive.unit); + } else { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_CHANNEL)) { + if (disk->info.addr.drive.target > 7) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU doesn't support target " + "greater than 7")); + goto error; + } + + if (disk->info.addr.drive.bus != 0 && + disk->info.addr.drive.unit != 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU only supports both bus and " + "unit equal to 0")); + goto error; + } + } + + virBufferAsprintf(&opt, ",bus=%s.0,channel=%d,scsi-id=%d,lun=%d", + contAlias, + disk->info.addr.drive.bus, + disk->info.addr.drive.target, + disk->info.addr.drive.unit); + } break; - case VIR_DOMAIN_CONTROLLER_TYPE_SATA: - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_AHCI)) { + case VIR_DOMAIN_DISK_BUS_SATA: + if (disk->info.addr.drive.bus != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("SATA is not supported with this " - "QEMU binary")); + _("bus must be 0 for ide controller")); goto error; } - virBufferAsprintf(&buf, "ahci,id=%s", def->info.alias); - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_USB: - if (qemuBuildUSBControllerDevStr(domainDef, def, qemuCaps, &buf) == -1) + if (disk->info.addr.drive.target != 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("target must be 0 for ide controller")); goto error; + } - if (nusbcontroller) - *nusbcontroller += 1; + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) + virBufferAddLit(&opt, "ide-cd"); + else + virBufferAddLit(&opt, "ide-hd"); + } else { + virBufferAddLit(&opt, "ide-drive"); + } + if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, + disk->info.addr.drive.controller))) + goto error; + virBufferAsprintf(&opt, ",bus=%s.%d", + contAlias, + disk->info.addr.drive.unit); break; - case VIR_DOMAIN_CONTROLLER_TYPE_PCI: - if (def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT || - def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) { + case VIR_DOMAIN_DISK_BUS_VIRTIO: + if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { + virBufferAddLit(&opt, "virtio-blk-ccw"); + if (disk->iothread) + virBufferAsprintf(&opt, ",iothread=iothread%u", disk->iothread); + } else if (disk->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { + virBufferAddLit(&opt, "virtio-blk-s390"); + } else if (disk->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) { + virBufferAddLit(&opt, "virtio-blk-device"); + } else { + virBufferAddLit(&opt, "virtio-blk-pci"); + if (disk->iothread) + virBufferAsprintf(&opt, ",iothread=iothread%u", disk->iothread); + } + qemuBuildIoEventFdStr(&opt, disk->ioeventfd, qemuCaps); + if (disk->event_idx && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_EVENT_IDX)) { + virBufferAsprintf(&opt, ",event_idx=%s", + virTristateSwitchTypeToString(disk->event_idx)); + } + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SCSI)) { + /* if sg_io is true but the scsi option isn't supported, + * that means it's just always on in this version of qemu. + */ + virBufferAsprintf(&opt, ",scsi=%s", + (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) + ? "on" : "off"); + } + if (qemuBuildDeviceAddressStr(&opt, def, &disk->info, qemuCaps) < 0) + goto error; + break; + + case VIR_DOMAIN_DISK_BUS_USB: + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("wrong function called for pci-root/pcie-root")); - return NULL; + _("unexpected address type for usb disk")); + goto error; } - if (def->idx == 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("index for pci controllers of model '%s' must be > 0"), - virDomainControllerModelPCITypeToString(def->model)); + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU doesn't support '-device " + "usb-storage'")); goto error; + } - switch (def->model) { - case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: - if (def->opts.pciopts.modelName - == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE || - def->opts.pciopts.chassisNr == -1) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("autogenerated pci-bridge options not set")); - goto error; - } + virBufferAddLit(&opt, "usb-storage"); - modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); - if (!modelName) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown pci-bridge model name value %d"), - def->opts.pciopts.modelName); - goto error; - } - if (def->opts.pciopts.modelName - != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("PCI controller model name '%s' " - "is not valid for a pci-bridge"), - modelName); - goto error; - } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("the pci-bridge controller " - "is not supported in this QEMU binary")); - goto error; - } - virBufferAsprintf(&buf, "%s,chassis_nr=%d,id=%s", - modelName, def->opts.pciopts.chassisNr, - def->info.alias); - break; - case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: - if (def->opts.pciopts.modelName - == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("autogenerated dmi-to-pci-bridge options not set")); - goto error; - } - - modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); - if (!modelName) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown dmi-to-pci-bridge model name value %d"), - def->opts.pciopts.modelName); - goto error; - } - if (def->opts.pciopts.modelName - != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("PCI controller model name '%s' " - "is not valid for a dmi-to-pci-bridge"), - modelName); - goto error; - } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("the dmi-to-pci-bridge (i82801b11-bridge) " - "controller is not supported in this QEMU binary")); - goto error; - } - virBufferAsprintf(&buf, "%s,id=%s", modelName, def->info.alias); - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: - if (def->opts.pciopts.modelName - == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("autogenerated pcie-root-port options not set")); - goto error; - } - modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); - if (!modelName) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown pcie-root-port model name value %d"), - def->opts.pciopts.modelName); - goto error; - } - if (def->opts.pciopts.modelName - != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("PCI controller model name '%s' " - "is not valid for a pcie-root-port"), - modelName); - goto error; - } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_IOH3420)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("the pcie-root-port (ioh3420) " - "controller is not supported in this QEMU binary")); - goto error; - } - - virBufferAsprintf(&buf, "%s,port=0x%x,chassis=%d,id=%s", - modelName, def->opts.pciopts.port, - def->opts.pciopts.chassis, def->info.alias); - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: - if (def->opts.pciopts.modelName - == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("autogenerated pcie-switch-upstream-port options not set")); - goto error; - } - modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); - if (!modelName) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown pcie-switch-upstream-port model name value %d"), - def->opts.pciopts.modelName); - goto error; - } - if (def->opts.pciopts.modelName - != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("PCI controller model name '%s' " - "is not valid for a pcie-switch-upstream-port"), - modelName); - goto error; - } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_X3130_UPSTREAM)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("the pcie-switch-upstream-port (x3130-upstream) " - "controller is not supported in this QEMU binary")); - goto error; - } - - virBufferAsprintf(&buf, "%s,id=%s", modelName, def->info.alias); - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: - if (def->opts.pciopts.modelName - == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE || - def->opts.pciopts.chassis == -1 || - def->opts.pciopts.port == -1) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("autogenerated pcie-switch-downstream-port " - "options not set")); - goto error; - } - - modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); - if (!modelName) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown pcie-switch-downstream-port model name value %d"), - def->opts.pciopts.modelName); - goto error; - } - if (def->opts.pciopts.modelName - != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("PCI controller model name '%s' " - "is not valid for a pcie-switch-downstream-port"), - modelName); - goto error; - } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_XIO3130_DOWNSTREAM)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("The pcie-switch-downstream-port " - "(xio3130-downstream) controller " - "is not supported in this QEMU binary")); - goto error; - } - virBufferAsprintf(&buf, "%s,port=0x%x,chassis=%d,id=%s", - modelName, def->opts.pciopts.port, - def->opts.pciopts.chassis, def->info.alias); - break; - } + if (qemuBuildDeviceAddressStr(&opt, def, &disk->info, qemuCaps) < 0) + goto error; break; - case VIR_DOMAIN_CONTROLLER_TYPE_IDE: - /* Since we currently only support the integrated IDE - * controller on various boards, if we ever get to here, it's - * because some other machinetype had an IDE controller - * specified, or one with a single IDE contraller had multiple - * ide controllers specified. - */ - if (qemuDomainMachineHasBuiltinIDE(domainDef)) - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Only a single IDE controller is supported " - "for this machine type")); - else - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("IDE controllers are unsupported for " - "this QEMU binary or machine type")); - goto error; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported controller type: %s"), - virDomainControllerTypeToString(def->type)); + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk bus '%s' with device setup"), bus); goto error; } - if (def->queues) - virBufferAsprintf(&buf, ",num_queues=%u", def->queues); + virBufferAsprintf(&opt, ",drive=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); + virBufferAsprintf(&opt, ",id=%s", disk->info.alias); + if (bootindex && virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX)) + virBufferAsprintf(&opt, ",bootindex=%d", bootindex); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKIO)) { + if (disk->blockio.logical_block_size > 0) + virBufferAsprintf(&opt, ",logical_block_size=%u", + disk->blockio.logical_block_size); + if (disk->blockio.physical_block_size > 0) + virBufferAsprintf(&opt, ",physical_block_size=%u", + disk->blockio.physical_block_size); + } - if (def->cmd_per_lun) - virBufferAsprintf(&buf, ",cmd_per_lun=%u", def->cmd_per_lun); + if (disk->wwn) { + if (STRPREFIX(disk->wwn, "0x")) + virBufferAsprintf(&opt, ",wwn=%s", disk->wwn); + else + virBufferAsprintf(&opt, ",wwn=0x%s", disk->wwn); + } - if (def->max_sectors) - virBufferAsprintf(&buf, ",max_sectors=%u", def->max_sectors); + if (disk->vendor) + virBufferAsprintf(&opt, ",vendor=%s", disk->vendor); - qemuBuildIoEventFdStr(&buf, def->ioeventfd, qemuCaps); + if (disk->product) + virBufferAsprintf(&opt, ",product=%s", disk->product); - if (qemuBuildDeviceAddressStr(&buf, domainDef, &def->info, qemuCaps) < 0) - goto error; + if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_STORAGE_REMOVABLE)) { + if (disk->removable == VIR_TRISTATE_SWITCH_ON) + virBufferAddLit(&opt, ",removable=on"); + else + virBufferAddLit(&opt, ",removable=off"); + } else { + if (disk->removable != VIR_TRISTATE_SWITCH_ABSENT) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU doesn't support setting the " + "removable flag of USB storage devices")); + goto error; + } + } + } - if (virBufferCheckError(&buf) < 0) + if (virBufferCheckError(&opt) < 0) goto error; - return virBufferContentAndReset(&buf); + return virBufferContentAndReset(&opt); error: - virBufferFreeAndReset(&buf); + virBufferFreeAndReset(&opt); return NULL; } -/** - * qemuBuildMemoryBackendStr: - * @size: size of the memory device in kibibytes - * @pagesize: size of the requested memory page in KiB, 0 for default - * @guestNode: NUMA node in the guest that the memory object will be attached - * to, or -1 if NUMA is not used in the guest - * @hostNodes: map of host nodes to alloc the memory in, NULL for default - * @autoNodeset: fallback nodeset in case of automatic numa placement - * @def: domain definition object - * @qemuCaps: qemu capabilities object - * @cfg: qemu driver config object - * @aliasPrefix: prefix string of the alias (to allow for multiple frontents) - * @id: index of the device (to construct the alias) - * @backendStr: returns the object string - * - * Formats the configuration string for the memory device backend according - * to the configuration. @pagesize and @hostNodes can be used to override the - * default source configuration, both are optional. - * - * Returns 0 on success, 1 if only the implicit memory-device-ram with no - * other configuration was used (to detect legacy configurations). Returns - * -1 in case of an error. - */ -int -qemuBuildMemoryBackendStr(unsigned long long size, - unsigned long long pagesize, - int guestNode, - virBitmapPtr userNodeset, - virBitmapPtr autoNodeset, - virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virQEMUDriverConfigPtr cfg, - const char **backendType, - virJSONValuePtr *backendProps, - bool force) +char *qemuBuildFSStr(virDomainFSDefPtr fs, + virQEMUCapsPtr qemuCaps ATTRIBUTE_UNUSED) { - virDomainHugePagePtr master_hugepage = NULL; - virDomainHugePagePtr hugepage = NULL; - virDomainNumatuneMemMode mode; - const long system_page_size = virGetSystemPageSizeKB(); - virNumaMemAccess memAccess = VIR_NUMA_MEM_ACCESS_DEFAULT; - size_t i; - char *mem_path = NULL; - virBitmapPtr nodemask = NULL; - int ret = -1; - virJSONValuePtr props = NULL; - bool nodeSpecified = virDomainNumatuneNodeSpecified(def->numa, guestNode); - - *backendProps = NULL; - *backendType = NULL; - - if (guestNode >= 0) { - /* memory devices could provide a invalid guest node */ - if (guestNode >= virDomainNumaGetNodeCount(def->numa)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("can't add memory backend for guest node '%d' as " - "the guest has only '%zu' NUMA nodes configured"), - guestNode, virDomainNumaGetNodeCount(def->numa)); - return -1; - } + virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *driver = qemuDomainFSDriverTypeToString(fs->fsdriver); + const char *wrpolicy = virDomainFSWrpolicyTypeToString(fs->wrpolicy); - memAccess = virDomainNumaGetNodeMemoryAccessMode(def->numa, guestNode); + if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("only supports mount filesystem type")); + goto error; } - if (virDomainNumatuneGetMode(def->numa, guestNode, &mode) < 0 && - virDomainNumatuneGetMode(def->numa, -1, &mode) < 0) - mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT; - - if (pagesize == 0) { - /* Find the huge page size we want to use */ - for (i = 0; i < def->mem.nhugepages; i++) { - bool thisHugepage = false; - - hugepage = &def->mem.hugepages[i]; - - if (!hugepage->nodemask) { - master_hugepage = hugepage; - continue; - } - - /* just find the master hugepage in case we don't use NUMA */ - if (guestNode < 0) - continue; - - if (virBitmapGetBit(hugepage->nodemask, guestNode, - &thisHugepage) < 0) { - /* Ignore this error. It's not an error after all. Well, - * the nodemask for this <page/> can contain lower NUMA - * nodes than we are querying in here. */ - continue; - } + if (!driver) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Filesystem driver type not supported")); + goto error; + } + virBufferAdd(&opt, driver, -1); - if (thisHugepage) { - /* Hooray, we've found the page size */ - break; - } + if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PATH || + fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT) { + if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_MAPPED) { + virBufferAddLit(&opt, ",security_model=mapped"); + } else if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) { + virBufferAddLit(&opt, ",security_model=passthrough"); + } else if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_SQUASH) { + virBufferAddLit(&opt, ",security_model=none"); } - - if (i == def->mem.nhugepages) { - /* We have not found specific huge page to be used with this - * NUMA node. Use the generic setting then (<page/> without any - * @nodemask) if possible. */ - hugepage = master_hugepage; + } else { + /* For other fs drivers, default(passthru) should always + * be supported */ + if (fs->accessmode != VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("only supports passthrough accessmode")); + goto error; } - - if (hugepage) - pagesize = hugepage->size; } - if (pagesize == system_page_size) { - /* However, if user specified to use "huge" page - * of regular system page size, it's as if they - * hasn't specified any huge pages at all. */ - pagesize = 0; - hugepage = NULL; + if (fs->wrpolicy) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_WRITEOUT)) { + virBufferAsprintf(&opt, ",writeout=%s", wrpolicy); + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("filesystem writeout not supported")); + goto error; + } } - if (!(props = virJSONValueNewObject())) - return -1; - - if (pagesize || hugepage) { - if (pagesize) { - /* Now lets see, if the huge page we want to use is even mounted - * and ready to use */ - for (i = 0; i < cfg->nhugetlbfs; i++) { - if (cfg->hugetlbfs[i].size == pagesize) - break; - } - - if (i == cfg->nhugetlbfs) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find any usable hugetlbfs mount for %llu KiB"), - pagesize); - goto cleanup; - } + virBufferAsprintf(&opt, ",id=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); + virBufferAsprintf(&opt, ",path=%s", fs->src); - if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[i]))) - goto cleanup; + if (fs->readonly) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_READONLY)) { + virBufferAddLit(&opt, ",readonly"); } else { - if (!(mem_path = qemuGetDefaultHugepath(cfg->hugetlbfs, - cfg->nhugetlbfs))) - goto cleanup; + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("readonly filesystem is not supported by this " + "QEMU binary")); + goto error; } + } - *backendType = "memory-backend-file"; - - if (virJSONValueObjectAdd(props, - "b:prealloc", true, - "s:mem-path", mem_path, - NULL) < 0) - goto cleanup; - - switch (memAccess) { - case VIR_NUMA_MEM_ACCESS_SHARED: - if (virJSONValueObjectAdd(props, "b:share", true, NULL) < 0) - goto cleanup; - break; + if (virBufferCheckError(&opt) < 0) + goto error; - case VIR_NUMA_MEM_ACCESS_PRIVATE: - if (virJSONValueObjectAdd(props, "b:share", false, NULL) < 0) - goto cleanup; - break; + return virBufferContentAndReset(&opt); - case VIR_NUMA_MEM_ACCESS_DEFAULT: - case VIR_NUMA_MEM_ACCESS_LAST: - break; - } - } else { - if (memAccess) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Shared memory mapping is supported " - "only with hugepages")); - goto cleanup; - } + error: + virBufferFreeAndReset(&opt); + return NULL; +} - *backendType = "memory-backend-ram"; - } - if (virJSONValueObjectAdd(props, "U:size", size * 1024, NULL) < 0) - goto cleanup; +char * +qemuBuildFSDevStr(virDomainDefPtr def, + virDomainFSDefPtr fs, + virQEMUCapsPtr qemuCaps) +{ + virBuffer opt = VIR_BUFFER_INITIALIZER; - if (userNodeset) { - nodemask = userNodeset; - } else { - if (virDomainNumatuneMaybeGetNodeset(def->numa, autoNodeset, - &nodemask, guestNode) < 0) - goto cleanup; + if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("can only passthrough directories")); + goto error; } - if (nodemask) { - if (!virNumaNodesetIsAvailable(nodemask)) - goto cleanup; - if (virJSONValueObjectAdd(props, - "m:host-nodes", nodemask, - "S:policy", qemuNumaPolicyTypeToString(mode), - NULL) < 0) - goto cleanup; - } + if (fs->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) + virBufferAddLit(&opt, "virtio-9p-ccw"); + else + virBufferAddLit(&opt, "virtio-9p-pci"); - /* If none of the following is requested... */ - if (!pagesize && !userNodeset && !memAccess && !nodeSpecified && !force) { - /* report back that using the new backend is not necessary - * to achieve the desired configuration */ - ret = 1; - } else { - /* otherwise check the required capability */ - if (STREQ(*backendType, "memory-backend-file") && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("this qemu doesn't support the " - "memory-backend-file object")); - goto cleanup; - } else if (STREQ(*backendType, "memory-backend-ram") && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("this qemu doesn't support the " - "memory-backend-ram object")); - goto cleanup; - } + virBufferAsprintf(&opt, ",id=%s", fs->info.alias); + virBufferAsprintf(&opt, ",fsdev=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); + virBufferAsprintf(&opt, ",mount_tag=%s", fs->dst); - ret = 0; - } + if (qemuBuildDeviceAddressStr(&opt, def, &fs->info, qemuCaps) < 0) + goto error; - *backendProps = props; - props = NULL; + if (virBufferCheckError(&opt) < 0) + goto error; - cleanup: - virJSONValueFree(props); - VIR_FREE(mem_path); + return virBufferContentAndReset(&opt); - return ret; + error: + virBufferFreeAndReset(&opt); + return NULL; } static int -qemuBuildMemoryCellBackendStr(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virQEMUDriverConfigPtr cfg, - size_t cell, - virBitmapPtr auto_nodeset, - char **backendStr) +qemuControllerModelUSBToCaps(int model) { - virJSONValuePtr props = NULL; - char *alias = NULL; - const char *backendType; - int ret = -1; - int rc; - unsigned long long memsize = virDomainNumaGetNodeMemorySize(def->numa, - cell); + switch (model) { + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI: + return QEMU_CAPS_PIIX3_USB_UHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI: + return QEMU_CAPS_PIIX4_USB_UHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_EHCI: + return QEMU_CAPS_USB_EHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3: + return QEMU_CAPS_ICH9_USB_EHCI1; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI: + return QEMU_CAPS_VT82C686B_USB_UHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI: + return QEMU_CAPS_PCI_OHCI; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI: + return QEMU_CAPS_NEC_USB_XHCI; + default: + return -1; + } +} - *backendStr = NULL; - if (virAsprintf(&alias, "ram-node%zu", cell) < 0) - goto cleanup; +static int +qemuBuildUSBControllerDevStr(virDomainDefPtr domainDef, + virDomainControllerDefPtr def, + virQEMUCapsPtr qemuCaps, + virBuffer *buf) +{ + const char *smodel; + int model, flags; - if ((rc = qemuBuildMemoryBackendStr(memsize, 0, cell, NULL, auto_nodeset, - def, qemuCaps, cfg, &backendType, - &props, false)) < 0) - goto cleanup; + model = def->model; - if (!(*backendStr = qemuBuildObjectCommandlineFromJSON(backendType, - alias, - props))) - goto cleanup; + if (model == -1) { + if ARCH_IS_PPC64(domainDef->os.arch) + model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI; + else + model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI; + } - ret = rc; + smodel = qemuControllerModelUSBTypeToString(model); + flags = qemuControllerModelUSBToCaps(model); - cleanup: - VIR_FREE(alias); - virJSONValueFree(props); + if (flags == -1 || !virQEMUCapsGet(qemuCaps, flags)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("%s not supported in this QEMU binary"), smodel); + return -1; + } - return ret; -} + virBufferAsprintf(buf, "%s", smodel); + if (def->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_USB) + virBufferAsprintf(buf, ",masterbus=%s.0,firstport=%d", + def->info.alias, def->info.master.usb.startport); + else + virBufferAsprintf(buf, ",id=%s", def->info.alias); -static char * -qemuBuildMemoryDimmBackendStr(virDomainMemoryDefPtr mem, - virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virQEMUDriverConfigPtr cfg) + return 0; +} + +char * +qemuBuildControllerDevStr(virDomainDefPtr domainDef, + virDomainControllerDefPtr def, + virQEMUCapsPtr qemuCaps, + int *nusbcontroller) { - virJSONValuePtr props = NULL; - char *alias = NULL; - const char *backendType; - char *ret = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + int model = def->model; + const char *modelName = NULL; - if (!mem->info.alias) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("memory device alias is not assigned")); + if (!qemuCheckCCWS390AddressSupport(domainDef, def->info, qemuCaps, + "controller")) return NULL; + + if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { + if ((qemuDomainSetSCSIControllerModel(domainDef, qemuCaps, &model)) < 0) + return NULL; } - if (virAsprintf(&alias, "mem%s", mem->info.alias) < 0) - goto cleanup; + if (!(def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI && + model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI)) { + if (def->queues) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'queues' is only supported by virtio-scsi controller")); + return NULL; + } + if (def->cmd_per_lun) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'cmd_per_lun' is only supported by virtio-scsi controller")); + return NULL; + } + if (def->max_sectors) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'max_sectors' is only supported by virtio-scsi controller")); + return NULL; + } + if (def->ioeventfd) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("'ioeventfd' is only supported by virtio-scsi controller")); + return NULL; + } + } - if (qemuBuildMemoryBackendStr(mem->size, mem->pagesize, - mem->targetNode, mem->sourceNodes, NULL, - def, qemuCaps, cfg, - &backendType, &props, true) < 0) - goto cleanup; + switch (def->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: + switch (model) { + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI: + if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) + virBufferAddLit(&buf, "virtio-scsi-ccw"); + else if (def->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) + virBufferAddLit(&buf, "virtio-scsi-s390"); + else if (def->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) + virBufferAddLit(&buf, "virtio-scsi-device"); + else + virBufferAddLit(&buf, "virtio-scsi-pci"); + break; + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC: + virBufferAddLit(&buf, "lsi"); + break; + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI: + virBufferAddLit(&buf, "spapr-vscsi"); + break; + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078: + virBufferAddLit(&buf, "megasas"); + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported controller model: %s"), + virDomainControllerModelSCSITypeToString(def->model)); + } + virBufferAsprintf(&buf, ",id=%s", def->info.alias); + break; - ret = qemuBuildObjectCommandlineFromJSON(backendType, alias, props); + case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: + if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + virBufferAddLit(&buf, "virtio-serial-pci"); + } else if (def->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { + virBufferAddLit(&buf, "virtio-serial-ccw"); + } else if (def->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { + virBufferAddLit(&buf, "virtio-serial-s390"); + } else if (def->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) { + virBufferAddLit(&buf, "virtio-serial-device"); + } else { + virBufferAddLit(&buf, "virtio-serial"); + } + virBufferAsprintf(&buf, ",id=%s", def->info.alias); + if (def->opts.vioserial.ports != -1) { + virBufferAsprintf(&buf, ",max_ports=%d", + def->opts.vioserial.ports); + } + if (def->opts.vioserial.vectors != -1) { + virBufferAsprintf(&buf, ",vectors=%d", + def->opts.vioserial.vectors); + } + break; - cleanup: - VIR_FREE(alias); - virJSONValueFree(props); + case VIR_DOMAIN_CONTROLLER_TYPE_CCID: + virBufferAsprintf(&buf, "usb-ccid,id=%s", def->info.alias); + break; - return ret; -} + case VIR_DOMAIN_CONTROLLER_TYPE_SATA: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_AHCI)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SATA is not supported with this " + "QEMU binary")); + goto error; + } + virBufferAsprintf(&buf, "ahci,id=%s", def->info.alias); + break; + case VIR_DOMAIN_CONTROLLER_TYPE_USB: + if (qemuBuildUSBControllerDevStr(domainDef, def, qemuCaps, &buf) == -1) + goto error; -char * -qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; + if (nusbcontroller) + *nusbcontroller += 1; + + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_PCI: + if (def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT || + def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("wrong function called for pci-root/pcie-root")); + return NULL; + } + if (def->idx == 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("index for pci controllers of model '%s' must be > 0"), + virDomainControllerModelPCITypeToString(def->model)); + goto error; + } + switch (def->model) { + case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: + if (def->opts.pciopts.modelName + == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE || + def->opts.pciopts.chassisNr == -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("autogenerated pci-bridge options not set")); + goto error; + } + + modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); + if (!modelName) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown pci-bridge model name value %d"), + def->opts.pciopts.modelName); + goto error; + } + if (def->opts.pciopts.modelName + != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("PCI controller model name '%s' " + "is not valid for a pci-bridge"), + modelName); + goto error; + } + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("the pci-bridge controller " + "is not supported in this QEMU binary")); + goto error; + } + virBufferAsprintf(&buf, "%s,chassis_nr=%d,id=%s", + modelName, def->opts.pciopts.chassisNr, + def->info.alias); + break; + case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: + if (def->opts.pciopts.modelName + == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("autogenerated dmi-to-pci-bridge options not set")); + goto error; + } + + modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); + if (!modelName) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown dmi-to-pci-bridge model name value %d"), + def->opts.pciopts.modelName); + goto error; + } + if (def->opts.pciopts.modelName + != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("PCI controller model name '%s' " + "is not valid for a dmi-to-pci-bridge"), + modelName); + goto error; + } + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("the dmi-to-pci-bridge (i82801b11-bridge) " + "controller is not supported in this QEMU binary")); + goto error; + } + virBufferAsprintf(&buf, "%s,id=%s", modelName, def->info.alias); + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: + if (def->opts.pciopts.modelName + == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("autogenerated pcie-root-port options not set")); + goto error; + } + modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); + if (!modelName) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown pcie-root-port model name value %d"), + def->opts.pciopts.modelName); + goto error; + } + if (def->opts.pciopts.modelName + != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("PCI controller model name '%s' " + "is not valid for a pcie-root-port"), + modelName); + goto error; + } + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_IOH3420)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("the pcie-root-port (ioh3420) " + "controller is not supported in this QEMU binary")); + goto error; + } + + virBufferAsprintf(&buf, "%s,port=0x%x,chassis=%d,id=%s", + modelName, def->opts.pciopts.port, + def->opts.pciopts.chassis, def->info.alias); + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: + if (def->opts.pciopts.modelName + == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("autogenerated pcie-switch-upstream-port options not set")); + goto error; + } + modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); + if (!modelName) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown pcie-switch-upstream-port model name value %d"), + def->opts.pciopts.modelName); + goto error; + } + if (def->opts.pciopts.modelName + != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("PCI controller model name '%s' " + "is not valid for a pcie-switch-upstream-port"), + modelName); + goto error; + } + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_X3130_UPSTREAM)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("the pcie-switch-upstream-port (x3130-upstream) " + "controller is not supported in this QEMU binary")); + goto error; + } + + virBufferAsprintf(&buf, "%s,id=%s", modelName, def->info.alias); + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: + if (def->opts.pciopts.modelName + == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE || + def->opts.pciopts.chassis == -1 || + def->opts.pciopts.port == -1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("autogenerated pcie-switch-downstream-port " + "options not set")); + goto error; + } - if (!mem->info.alias) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing alias for memory device")); - return NULL; - } + modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName); + if (!modelName) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown pcie-switch-downstream-port model name value %d"), + def->opts.pciopts.modelName); + goto error; + } + if (def->opts.pciopts.modelName + != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("PCI controller model name '%s' " + "is not valid for a pcie-switch-downstream-port"), + modelName); + goto error; + } + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_XIO3130_DOWNSTREAM)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("The pcie-switch-downstream-port " + "(xio3130-downstream) controller " + "is not supported in this QEMU binary")); + goto error; + } + virBufferAsprintf(&buf, "%s,port=0x%x,chassis=%d,id=%s", + modelName, def->opts.pciopts.port, + def->opts.pciopts.chassis, def->info.alias); + break; + } + break; - switch ((virDomainMemoryModel) mem->model) { - case VIR_DOMAIN_MEMORY_MODEL_DIMM: - virBufferAddLit(&buf, "pc-dimm,"); + case VIR_DOMAIN_CONTROLLER_TYPE_IDE: + /* Since we currently only support the integrated IDE + * controller on various boards, if we ever get to here, it's + * because some other machinetype had an IDE controller + * specified, or one with a single IDE contraller had multiple + * ide controllers specified. + */ + if (qemuDomainMachineHasBuiltinIDE(domainDef)) + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only a single IDE controller is supported " + "for this machine type")); + else + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("IDE controllers are unsupported for " + "this QEMU binary or machine type")); + goto error; - if (mem->targetNode >= 0) - virBufferAsprintf(&buf, "node=%d,", mem->targetNode); + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported controller type: %s"), + virDomainControllerTypeToString(def->type)); + goto error; + } - virBufferAsprintf(&buf, "memdev=mem%s,id=%s", - mem->info.alias, mem->info.alias); + if (def->queues) + virBufferAsprintf(&buf, ",num_queues=%u", def->queues); - if (mem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM) { - virBufferAsprintf(&buf, ",slot=%d", mem->info.addr.dimm.slot); - virBufferAsprintf(&buf, ",addr=%llu", mem->info.addr.dimm.base); - } + if (def->cmd_per_lun) + virBufferAsprintf(&buf, ",cmd_per_lun=%u", def->cmd_per_lun); - break; + if (def->max_sectors) + virBufferAsprintf(&buf, ",max_sectors=%u", def->max_sectors); - case VIR_DOMAIN_MEMORY_MODEL_NONE: - case VIR_DOMAIN_MEMORY_MODEL_LAST: - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("invalid memory device type")); - break; + qemuBuildIoEventFdStr(&buf, def->ioeventfd, qemuCaps); - } + if (qemuBuildDeviceAddressStr(&buf, domainDef, &def->info, qemuCaps) < 0) + goto error; if (virBufferCheckError(&buf) < 0) - return NULL; + goto error; return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; } @@ -5483,148 +5671,6 @@ qemuBuildClockArgStr(virDomainClockDefPtr def) } static int -qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg, - virDomainDefPtr def, - virCommandPtr cmd, - virQEMUCapsPtr qemuCaps, - virBitmapPtr auto_nodeset) -{ - size_t i; - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *cpumask = NULL, *tmpmask = NULL, *next = NULL; - char **nodeBackends = NULL; - bool needBackend = false; - int rc; - int ret = -1; - size_t ncells = virDomainNumaGetNodeCount(def->numa); - const long system_page_size = virGetSystemPageSizeKB(); - - if (virDomainNumatuneHasPerNodeBinding(def->numa) && - !(virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) || - virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Per-node memory binding is not supported " - "with this QEMU")); - goto cleanup; - } - - if (def->mem.nhugepages && - def->mem.hugepages[0].size != system_page_size && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("huge pages per NUMA node are not " - "supported with this QEMU")); - goto cleanup; - } - - if (!virDomainNumatuneNodesetIsAvailable(def->numa, auto_nodeset)) - goto cleanup; - - for (i = 0; i < def->mem.nhugepages; i++) { - ssize_t next_bit, pos = 0; - - if (!def->mem.hugepages[i].nodemask) { - /* This is the master hugepage to use. Skip it as it has no - * nodemask anyway. */ - continue; - } - - if (ncells) { - /* Fortunately, we allow only guest NUMA nodes to be continuous - * starting from zero. */ - pos = ncells - 1; - } - - next_bit = virBitmapNextSetBit(def->mem.hugepages[i].nodemask, pos); - if (next_bit >= 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("hugepages: node %zd not found"), - next_bit); - goto cleanup; - } - } - - if (VIR_ALLOC_N(nodeBackends, ncells) < 0) - goto cleanup; - - /* using of -numa memdev= cannot be combined with -numa mem=, thus we - * need to check which approach to use */ - for (i = 0; i < ncells; i++) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) || - virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) { - if ((rc = qemuBuildMemoryCellBackendStr(def, qemuCaps, cfg, i, - auto_nodeset, - &nodeBackends[i])) < 0) - goto cleanup; - - if (rc == 0) - needBackend = true; - } else { - if (virDomainNumaGetNodeMemoryAccessMode(def->numa, i)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Shared memory mapping is not supported " - "with this QEMU")); - goto cleanup; - } - } - } - - if (!needBackend && - qemuBuildMemPathStr(cfg, def, qemuCaps, cmd) < 0) - goto cleanup; - - for (i = 0; i < ncells; i++) { - VIR_FREE(cpumask); - if (!(cpumask = virBitmapFormat(virDomainNumaGetNodeCpumask(def->numa, i)))) - goto cleanup; - - if (strchr(cpumask, ',') && - !virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disjoint NUMA cpu ranges are not supported " - "with this QEMU")); - goto cleanup; - } - - if (needBackend) - virCommandAddArgList(cmd, "-object", nodeBackends[i], NULL); - - virCommandAddArg(cmd, "-numa"); - virBufferAsprintf(&buf, "node,nodeid=%zu", i); - - for (tmpmask = cpumask; tmpmask; tmpmask = next) { - if ((next = strchr(tmpmask, ','))) - *(next++) = '\0'; - virBufferAddLit(&buf, ",cpus="); - virBufferAdd(&buf, tmpmask, -1); - } - - if (needBackend) - virBufferAsprintf(&buf, ",memdev=ram-node%zu", i); - else - virBufferAsprintf(&buf, ",mem=%llu", - virDomainNumaGetNodeMemorySize(def->numa, i) / 1024); - - virCommandAddArgBuffer(cmd, &buf); - } - ret = 0; - - cleanup: - VIR_FREE(cpumask); - - if (nodeBackends) { - for (i = 0; i < ncells; i++) - VIR_FREE(nodeBackends[i]); - - VIR_FREE(nodeBackends); - } - - virBufferFreeAndReset(&buf); - return ret; -} - - -static int qemuBuildGraphicsVNCCommandLine(virQEMUDriverConfigPtr cfg, virCommandPtr cmd, virDomainDefPtr def, @@ -6963,32 +7009,8 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuBuildIOThreadCommandLine(cmd, def, qemuCaps) < 0) goto error; - if (virDomainNumaGetNodeCount(def->numa) && - qemuBuildNumaArgStr(cfg, def, cmd, qemuCaps, nodeset) < 0) - goto error; - - /* memory hotplug requires NUMA to be enabled - we already checked - * that memory devices are present only when NUMA is */ - - - for (i = 0; i < def->nmems; i++) { - char *backStr; - char *dimmStr; - - if (!(backStr = qemuBuildMemoryDimmBackendStr(def->mems[i], def, - qemuCaps, cfg))) - goto error; - - if (!(dimmStr = qemuBuildMemoryDeviceStr(def->mems[i]))) { - VIR_FREE(backStr); - goto error; - } - - virCommandAddArgList(cmd, "-object", backStr, "-device", dimmStr, NULL); - - VIR_FREE(backStr); - VIR_FREE(dimmStr); - } + if (qemuBuildNumaCommandLine(cmd, cfg, def, qemuCaps, nodeset) < 0) + goto error; virCommandAddArgList(cmd, "-uuid", uuid, NULL); -- 2.5.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list