[libvirt] [PATCH 00/10] Reorganizing qemu_command.c continued

Figure I'll do these in smaller chunks - it's a large module. Suffice to say there's a lot of "movement" and patches for each is tedius, but I hope in the end it will be easier to deal with. For the most part all patches are primarily code motion, although git diff may have you believe otherwise. Essentially, I'm starting at the top of qemuBuildCommandLine and working my way through either moving functions in a top down order from the beginning of the module or more usually creating a qemuBuildXXXXCommandLine function and calling it from the main processing code. I did not "manage" any of the QEMU_CAPS_DEVICE changes (e.g, remove code), but I could while I'm at if that's desired. Hopefully not many people are touching this module right now ;-)! John Ferlan (10): qemu: Move qemuDeviceDriveHostAlias qemu: Remove local emulator qemu: Make basic upfront checks before creating command qemu: Reorganize -machine arguments qemu: Reorganize -cpu arguments qemu: Reorganize qemuBuildDomainLoaderCommandLine call qemu: Reorganize -m arguments qemu: Reorganize -smp argument qemu: Reorganize IOThread -object arguments qemu: Reorganize -numa arguments src/qemu/qemu_alias.c | 16 + src/qemu/qemu_alias.h | 4 + src/qemu/qemu_command.c | 9010 ++++++++++++++++++++++++----------------------- src/qemu/qemu_command.h | 3 - src/qemu/qemu_hotplug.c | 6 +- 5 files changed, 4583 insertions(+), 4456 deletions(-) -- 2.5.0

Move function to qemu_alias.c, rename it to qemuDomainDeviceDriveAlias Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_alias.c | 16 ++++++++++++++++ src/qemu/qemu_alias.h | 4 ++++ src/qemu/qemu_command.c | 15 --------------- src/qemu/qemu_command.h | 3 --- src/qemu/qemu_hotplug.c | 6 +++--- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index efd9222..26d158b 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -32,6 +32,22 @@ VIR_LOG_INIT("qemu.qemu_alias"); +char * +qemuDomainDeviceDriveAlias(virDomainDiskDefPtr disk, + virQEMUCapsPtr qemuCaps) +{ + char *ret; + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { + ignore_value(virAsprintf(&ret, "%s%s", QEMU_DRIVE_HOST_PREFIX, + disk->info.alias)); + } else { + ignore_value(VIR_STRDUP(ret, disk->info.alias)); + } + return ret; +} + + int qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info, const char *prefix) diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index a2eaa27..ece3b6a 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -27,9 +27,13 @@ # include "domain_conf.h" # include "qemu_capabilities.h" +# include "qemu_command.h" # include "qemu_domain.h" # include "qemu_domain_address.h" +char *qemuDomainDeviceDriveAlias(virDomainDiskDefPtr disk, + virQEMUCapsPtr qemuCaps); + int qemuAssignDeviceChrAlias(virDomainDefPtr def, virDomainChrDefPtr chr, ssize_t idx); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 679f558..a9b5c3b 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -259,21 +259,6 @@ qemuBuildObjectCommandlineFromJSON(const char *type, } -char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, - virQEMUCapsPtr qemuCaps) -{ - char *ret; - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { - ignore_value(virAsprintf(&ret, "%s%s", QEMU_DRIVE_HOST_PREFIX, - disk->info.alias)); - } else { - ignore_value(VIR_STRDUP(ret, disk->info.alias)); - } - return ret; -} - - static int qemuBuildDeviceAddressStr(virBufferPtr buf, virDomainDefPtr domainDef, diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 776954e..6b3e68b 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -120,9 +120,6 @@ char *qemuBuildNicDevStr(virDomainDefPtr def, size_t vhostfdSize, virQEMUCapsPtr qemuCaps); -char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, - virQEMUCapsPtr qemuCaps); - /* Both legacy & current support */ char *qemuBuildDriveStr(virConnectPtr conn, virDomainDiskDefPtr disk, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ee305e7..831252d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1,7 +1,7 @@ /* * qemu_hotplug.c: QEMU device hotplug management * - * Copyright (C) 2006-2015 Red Hat, Inc. + * Copyright (C) 2006-2016 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -193,7 +193,7 @@ qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver, if (qemuDomainPrepareDisk(driver, vm, disk, newsrc, false) < 0) goto cleanup; - if (!(driveAlias = qemuDeviceDriveHostAlias(disk, priv->qemuCaps))) + if (!(driveAlias = qemuDomainDeviceDriveAlias(disk, priv->qemuCaps))) goto error; do { @@ -359,7 +359,7 @@ qemuDomainAttachVirtioDiskDevice(virConnectPtr conn, if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->qemuCaps))) goto error; - if (!(drivealias = qemuDeviceDriveHostAlias(disk, priv->qemuCaps))) + if (!(drivealias = qemuDomainDeviceDriveAlias(disk, priv->qemuCaps))) goto error; if (!(devstr = qemuBuildDriveDevStr(vm->def, disk, 0, priv->qemuCaps))) -- 2.5.0

On Tue, Feb 16, 2016 at 19:44:11 -0500, John Ferlan wrote:
Move function to qemu_alias.c, rename it to qemuDomainDeviceDriveAlias
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_alias.c | 16 ++++++++++++++++ src/qemu/qemu_alias.h | 4 ++++ src/qemu/qemu_command.c | 15 --------------- src/qemu/qemu_command.h | 3 --- src/qemu/qemu_hotplug.c | 6 +++--- 5 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index efd9222..26d158b 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -32,6 +32,22 @@
VIR_LOG_INIT("qemu.qemu_alias");
+char * +qemuDomainDeviceDriveAlias(virDomainDiskDefPtr disk, + virQEMUCapsPtr qemuCaps) +{ + char *ret; + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { + ignore_value(virAsprintf(&ret, "%s%s", QEMU_DRIVE_HOST_PREFIX, + disk->info.alias)); + } else { + ignore_value(VIR_STRDUP(ret, disk->info.alias)); + } + return ret; +} + + int qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info, const char *prefix) diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index a2eaa27..ece3b6a 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -27,9 +27,13 @@ # include "domain_conf.h"
# include "qemu_capabilities.h" +# include "qemu_command.h"
So this is for QEMU_DRIVE_HOST_PREFIX. Shouldn't that macro be moved too? Or perhaps this function stay where it was? At very least, it's necessary in the code, not in the header to declare this function.
# include "qemu_domain.h" # include "qemu_domain_address.h"
+char *qemuDomainDeviceDriveAlias(virDomainDiskDefPtr disk, + virQEMUCapsPtr qemuCaps); + int qemuAssignDeviceChrAlias(virDomainDefPtr def, virDomainChrDefPtr chr, ssize_t idx);
Peter

Remove the local variable 'emulator' and just use def->emulator Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index a9b5c3b..ab27619 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4760,7 +4760,6 @@ qemuBuildCpuModelArgStr(virQEMUDriverPtr driver, static int qemuBuildCpuArgStr(virQEMUDriverPtr driver, const virDomainDef *def, - const char *emulator, virQEMUCapsPtr qemuCaps, virArch hostarch, char **opt, @@ -4801,8 +4800,8 @@ qemuBuildCpuArgStr(virQEMUDriverPtr driver, */ if (def->os.arch == VIR_ARCH_I686 && ((hostarch == VIR_ARCH_X86_64 && - strstr(emulator, "kvm")) || - strstr(emulator, "x86_64"))) { + strstr(def->emulator, "kvm")) || + strstr(def->emulator, "x86_64"))) { virBufferAdd(&buf, default_model, -1); have_cpu = true; } @@ -6624,7 +6623,6 @@ qemuBuildCommandLine(virConnectPtr conn, { virErrorPtr originalError = NULL; size_t i, j; - const char *emulator; char uuid[VIR_UUID_STRING_BUFLEN]; char *cpu; char *smp; @@ -6679,8 +6677,6 @@ qemuBuildCommandLine(virConnectPtr conn, virUUIDFormat(def->uuid, uuid); - emulator = def->emulator; - if (!virQEMUDriverIsPrivileged(driver)) { /* If we have no cgroups then we can have no tunings that * require them */ @@ -6731,7 +6727,7 @@ qemuBuildCommandLine(virConnectPtr conn, (def->virtType == VIR_DOMAIN_VIRT_QEMU)) virQEMUCapsClear(qemuCaps, QEMU_CAPS_DRIVE_BOOT); - cmd = virCommandNew(emulator); + cmd = virCommandNew(def->emulator); virCommandAddEnvPassCommon(cmd); @@ -6753,7 +6749,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuBuildMachineArgStr(cmd, def, qemuCaps) < 0) goto error; - if (qemuBuildCpuArgStr(driver, def, emulator, qemuCaps, + if (qemuBuildCpuArgStr(driver, def, qemuCaps, hostarch, &cpu, &hasHwVirt, !!migrateURI) < 0) goto error; @@ -6878,7 +6874,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SMBIOS_TYPE)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("the QEMU binary %s does not support smbios settings"), - emulator); + def->emulator); goto error; } @@ -8003,7 +7999,7 @@ qemuBuildCommandLine(virConnectPtr conn, } if (def->tpm) { - if (qemuBuildTPMCommandLine(def, cmd, qemuCaps, emulator) < 0) + if (qemuBuildTPMCommandLine(def, cmd, qemuCaps, def->emulator) < 0) goto error; } -- 2.5.0

On Tue, Feb 16, 2016 at 19:44:12 -0500, John Ferlan wrote:
Remove the local variable 'emulator' and just use def->emulator
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)
ACK

Create qemuBuildPreCheck to make some basic upfront checks before trying to build the command. Unfortunately the 'spice' count was used later on, so yuck, handle that. This will move some logic from much later to much earlier - we shouldn't be adjusting any data so that shouldn't matter. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 169 +++++++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 72 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ab27619..36fe110 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6593,6 +6593,99 @@ qemuBuildTPMCommandLine(virDomainDefPtr def, } +/* + * qemuBuildPreCheck: + * + * Perform some basic configuration checks before taking the plunge. + */ +static int +qemuBuildPreCheck(virQEMUDriverPtr driver, + const virDomainDef *def, + int *spicecnt) +{ + size_t i; + int sdl = 0; + int vnc = 0; + int spice = 0; + + if (!virQEMUDriverIsPrivileged(driver)) { + /* If we have no cgroups then we can have no tunings that + * require them */ + + if (virMemoryLimitIsSet(def->mem.hard_limit) || + virMemoryLimitIsSet(def->mem.soft_limit) || + def->mem.min_guarantee || + virMemoryLimitIsSet(def->mem.swap_hard_limit)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Memory tuning is not available in session mode")); + goto error; + } + + if (def->blkio.weight) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Block I/O tuning is not available in session mode")); + goto error; + } + + if (def->cputune.sharesSpecified || def->cputune.period || + def->cputune.quota || def->cputune.emulator_period || + def->cputune.emulator_quota) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("CPU tuning is not available in session mode")); + goto error; + } + } + + for (i = 0; i < def->ngraphics; ++i) { + switch (def->graphics[i]->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + ++sdl; + break; + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + ++vnc; + break; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + ++spice; + break; + } + } + + if (sdl > 1 || vnc > 1 || spice > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("only 1 graphics device of each type " + "(sdl, vnc, spice) is supported")); + goto error; + } + *spicecnt = spice; + + if (def->virtType == VIR_DOMAIN_VIRT_XEN || + def->os.type == VIR_DOMAIN_OSTYPE_XEN || + def->os.type == VIR_DOMAIN_OSTYPE_LINUX) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("qemu emulator '%s' does not support xen"), + def->emulator); + goto error; + } + + for (i = 0; i < def->ndisks; i++) { + virDomainDiskDefPtr disk = def->disks[i]; + + if (disk->src->driverName != NULL && + STRNEQ(disk->src->driverName, "qemu")) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported driver name '%s' for disk '%s'"), + disk->src->driverName, disk->src->path); + goto error; + } + } + + return 0; + + error: + return -1; +} + + qemuBuildCommandLineCallbacks buildCommandLineCallbacks = { .qemuGetSCSIDeviceSgName = virSCSIDeviceGetSgName, }; @@ -6626,14 +6719,12 @@ qemuBuildCommandLine(virConnectPtr conn, char uuid[VIR_UUID_STRING_BUFLEN]; char *cpu; char *smp; + int spicecnt = 0; int last_good_net = -1; bool hasHwVirt = false; virCommandPtr cmd = NULL; bool allowReboot = true; bool emitBootindex = false; - int sdl = 0; - int vnc = 0; - int spice = 0; int usbcontroller = 0; int actualSerials = 0; bool usblegacy = false; @@ -6677,47 +6768,8 @@ qemuBuildCommandLine(virConnectPtr conn, virUUIDFormat(def->uuid, uuid); - if (!virQEMUDriverIsPrivileged(driver)) { - /* If we have no cgroups then we can have no tunings that - * require them */ - - if (virMemoryLimitIsSet(def->mem.hard_limit) || - virMemoryLimitIsSet(def->mem.soft_limit) || - def->mem.min_guarantee || - virMemoryLimitIsSet(def->mem.swap_hard_limit)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Memory tuning is not available in session mode")); - goto error; - } - - if (def->blkio.weight) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Block I/O tuning is not available in session mode")); - goto error; - } - - if (def->cputune.sharesSpecified || def->cputune.period || - def->cputune.quota || def->cputune.emulator_period || - def->cputune.emulator_quota) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("CPU tuning is not available in session mode")); - goto error; - } - } - - for (i = 0; i < def->ngraphics; ++i) { - switch (def->graphics[i]->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - ++sdl; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - ++vnc; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - ++spice; - break; - } - } + if (qemuBuildPrecCheck(driver, def, &spicecnt) < 0) + goto error; /* * do not use boot=on for drives when not using KVM since this @@ -6857,14 +6909,6 @@ qemuBuildCommandLine(virConnectPtr conn, } virCommandAddArgList(cmd, "-uuid", uuid, NULL); - if (def->virtType == VIR_DOMAIN_VIRT_XEN || - def->os.type == VIR_DOMAIN_OSTYPE_XEN || - def->os.type == VIR_DOMAIN_OSTYPE_LINUX) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("qemu emulator '%s' does not support xen"), - def->emulator); - goto error; - } if ((def->os.smbios_mode != VIR_DOMAIN_SMBIOS_NONE) && (def->os.smbios_mode != VIR_DOMAIN_SMBIOS_EMULATE)) { @@ -7379,18 +7423,6 @@ qemuBuildCommandLine(virConnectPtr conn, } } - for (i = 0; i < def->ndisks; i++) { - virDomainDiskDefPtr disk = def->disks[i]; - - if (disk->src->driverName != NULL && - STRNEQ(disk->src->driverName, "qemu")) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported driver name '%s' for disk '%s'"), - disk->src->driverName, disk->src->path); - goto error; - } - } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { for (j = 0; j < ARRAY_CARDINALITY(contOrder); j++) { for (i = 0; i < def->ncontrollers; i++) { @@ -7803,7 +7835,7 @@ qemuBuildCommandLine(virConnectPtr conn, virDomainChrDefPtr serial = def->serials[i]; char *devstr; - if (serial->source.type == VIR_DOMAIN_CHR_TYPE_SPICEPORT && !spice) + if (serial->source.type == VIR_DOMAIN_CHR_TYPE_SPICEPORT && !spicecnt) continue; /* Use -chardev with -device if they are available */ @@ -8037,13 +8069,6 @@ qemuBuildCommandLine(virConnectPtr conn, } } - if (sdl > 1 || vnc > 1 || spice > 1) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("only 1 graphics device of each type " - "(sdl, vnc, spice) is supported")); - goto error; - } - for (i = 0; i < def->ngraphics; ++i) { if (qemuBuildGraphicsCommandLine(cfg, cmd, def, qemuCaps, def->graphics[i]) < 0) -- 2.5.0

On Tue, Feb 16, 2016 at 19:44:13 -0500, John Ferlan wrote:
Create qemuBuildPreCheck to make some basic upfront checks before trying to build the command.
Unfortunately the 'spice' count was used later on, so yuck, handle that.
The count is not needed. Just the fact that there are spice devices is required. Additionally I don't understand why spice-transported serial ports are skipped rather than pointed out if there is no spice graphics to transport them. I'd fix that one first rather than having to deal with the pointer and having a checker that needs to leak stuff out to the caller.
This will move some logic from much later to much earlier - we shouldn't be adjusting any data so that shouldn't matter.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 169 +++++++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 72 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ab27619..36fe110 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6593,6 +6593,99 @@ qemuBuildTPMCommandLine(virDomainDefPtr def, }
+/* + * qemuBuildPreCheck:
I'd prefer qemuBuildCommandLineValidate
+ * + * Perform some basic configuration checks before taking the plunge.
And something either more descriptive or at least finishing the sentence after 'checks'.
+ */ +static int +qemuBuildPreCheck(virQEMUDriverPtr driver, + const virDomainDef *def, + int *spicecnt)
Checks should not leak any value.
+{ + size_t i; + int sdl = 0; + int vnc = 0; + int spice = 0;
[...]
+ + + return 0; + + error: + return -1;
Nothing to clean up, thus the label doesn't make sense.
+} + + qemuBuildCommandLineCallbacks buildCommandLineCallbacks = { .qemuGetSCSIDeviceSgName = virSCSIDeviceGetSgName, };
[...]
@@ -6677,47 +6768,8 @@ qemuBuildCommandLine(virConnectPtr conn,
- - for (i = 0; i < def->ngraphics; ++i) { - switch (def->graphics[i]->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - ++sdl; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - ++vnc; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - ++spice; - break; - } - } + if (qemuBuildPrecCheck(driver, def, &spicecnt) < 0)
This won't compile.
+ goto error;
/* * do not use boot=on for drives when not using KVM since this
Peter

On 02/17/2016 03:13 AM, Peter Krempa wrote:
On Tue, Feb 16, 2016 at 19:44:13 -0500, John Ferlan wrote:
Create qemuBuildPreCheck to make some basic upfront checks before trying to build the command.
Unfortunately the 'spice' count was used later on, so yuck, handle that.
The count is not needed. Just the fact that there are spice devices is required. Additionally I don't understand why spice-transported serial ports are skipped rather than pointed out if there is no spice graphics to transport them. I'd fix that one first rather than having to deal with the pointer and having a checker that needs to leak stuff out to the caller.
Later in the code, they are just ignored: for (i = 0; i < def->nserials; i++) { virDomainChrDefPtr serial = def->serials[i]; char *devstr; if (serial->source.type == VIR_DOMAIN_CHR_TYPE_SPICEPORT && !spicecnt) continue; I'm not familiar with a spiceport - so I left things as is except of course for keeping track that there is one. Looks like the code was introduced by Martin (commit id 'd27e6bc40') and it's designed that way. There's even a test for it. So it seems my alternative is to perform that same ngraphics loop later on either as a precursor to the nserials loop or within the loop only if a SPICEPORT was found. Or of course leak/return a bool hasSpice.
This will move some logic from much later to much earlier - we shouldn't be adjusting any data so that shouldn't matter.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 169 +++++++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 72 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ab27619..36fe110 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6593,6 +6593,99 @@ qemuBuildTPMCommandLine(virDomainDefPtr def, }
+/* + * qemuBuildPreCheck:
I'd prefer qemuBuildCommandLineValidate
+ * + * Perform some basic configuration checks before taking the plunge.
And something either more descriptive or at least finishing the sentence after 'checks'.
+ */ +static int +qemuBuildPreCheck(virQEMUDriverPtr driver, + const virDomainDef *def, + int *spicecnt)
Checks should not leak any value.
+{ + size_t i; + int sdl = 0; + int vnc = 0; + int spice = 0;
[...]
+ + + return 0; + + error: + return -1;
Nothing to clean up, thus the label doesn't make sense.
OK - rather than goto error's, I'll replace with return -1 (6 of one, half dozen of another)
+} + + qemuBuildCommandLineCallbacks buildCommandLineCallbacks = { .qemuGetSCSIDeviceSgName = virSCSIDeviceGetSgName, };
[...]
@@ -6677,47 +6768,8 @@ qemuBuildCommandLine(virConnectPtr conn,
- - for (i = 0; i < def->ngraphics; ++i) { - switch (def->graphics[i]->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - ++sdl; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - ++vnc; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - ++spice; - break; - } - } + if (qemuBuildPrecCheck(driver, def, &spicecnt) < 0)
This won't compile.
Ugh... My bad - I had a different and much worse name, changed it at the last second - at least it was not a change name, then push, then leave for the day ;-) Tks - John

Reorganize the module to put all the -machine argument processing code together at the top to form a logical order of processing for qemuBuildCommandLine working top down in the module. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 464 ++++++++++++++++++++++++------------------------ 1 file changed, 235 insertions(+), 229 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 36fe110..ede651a 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -149,6 +149,240 @@ VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST, "preferred", "interleave"); + +/** Start -machine arguments */ +static int +qemuBuildObsoleteAccelArg(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + bool disableKVM = false; + bool enableKVM = false; + + switch (def->virtType) { + case VIR_DOMAIN_VIRT_QEMU: + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) + disableKVM = true; + break; + + case VIR_DOMAIN_VIRT_KQEMU: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("the QEMU binary does not support kqemu")); + break; + + case VIR_DOMAIN_VIRT_KVM: + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ENABLE_KVM)) { + enableKVM = true; + } else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("the QEMU binary does not support kvm")); + return -1; + } + break; + + case VIR_DOMAIN_VIRT_XEN: + /* XXX better check for xenner */ + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary does not support %s"), + virDomainVirtTypeToString(def->virtType)); + return -1; + } + + if (disableKVM) + virCommandAddArg(cmd, "-no-kvm"); + if (enableKVM) + virCommandAddArg(cmd, "-enable-kvm"); + + return 0; +} + + +static bool +qemuAppendKeyWrapMachineParm(virBuffer *buf, virQEMUCapsPtr qemuCaps, + int flag, const char *pname, int pstate) +{ + if (pstate != VIR_TRISTATE_SWITCH_ABSENT) { + if (!virQEMUCapsGet(qemuCaps, flag)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("%s is not available with this QEMU binary"), + pname); + return false; + } + + virBufferAsprintf(buf, ",%s=%s", pname, + virTristateSwitchTypeToString(pstate)); + } + + return true; +} + + +static bool +qemuAppendKeyWrapMachineParms(virBuffer *buf, virQEMUCapsPtr qemuCaps, + const virDomainKeyWrapDef *keywrap) +{ + if (!qemuAppendKeyWrapMachineParm(buf, qemuCaps, QEMU_CAPS_AES_KEY_WRAP, + "aes-key-wrap", keywrap->aes)) + return false; + + if (!qemuAppendKeyWrapMachineParm(buf, qemuCaps, QEMU_CAPS_DEA_KEY_WRAP, + "dea-key-wrap", keywrap->dea)) + return false; + + return true; +} + + +static int +qemuBuildMachineCommandLine(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + bool obsoleteAccel = false; + + /* This should *never* be NULL, since we always provide + * a machine in the capabilities data for QEMU. So this + * check is just here as a safety in case the unexpected + * happens */ + if (!def->os.machine) + return 0; + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_OPT)) { + /* if no parameter to the machine type is needed, we still use + * '-M' to keep the most of the compatibility with older versions. + */ + virCommandAddArgList(cmd, "-M", def->os.machine, NULL); + if (def->mem.dump_core) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("dump-guest-core is not available " + "with this QEMU binary")); + return -1; + } + + if (def->mem.nosharepages) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("disable shared memory is not available " + "with this QEMU binary")); + return -1; + } + + obsoleteAccel = true; + + if (def->keywrap) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("key wrap support is not available " + "with this QEMU binary")); + return -1; + } + } else { + virBuffer buf = VIR_BUFFER_INITIALIZER; + virTristateSwitch vmport = def->features[VIR_DOMAIN_FEATURE_VMPORT]; + + virCommandAddArg(cmd, "-machine"); + virBufferAdd(&buf, def->os.machine, -1); + + if (def->virtType == VIR_DOMAIN_VIRT_QEMU) + virBufferAddLit(&buf, ",accel=tcg"); + else if (def->virtType == VIR_DOMAIN_VIRT_KVM) + virBufferAddLit(&buf, ",accel=kvm"); + else + obsoleteAccel = true; + + /* To avoid the collision of creating USB controllers when calling + * machine->init in QEMU, it needs to set usb=off + */ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_USB_OPT)) + virBufferAddLit(&buf, ",usb=off"); + + if (vmport) { + if (!virQEMUCapsSupportsVmport(qemuCaps, def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vmport is not available " + "with this QEMU binary")); + virBufferFreeAndReset(&buf); + return -1; + } + + virBufferAsprintf(&buf, ",vmport=%s", + virTristateSwitchTypeToString(vmport)); + } + + if (def->mem.dump_core) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DUMP_GUEST_CORE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("dump-guest-core is not available " + "with this QEMU binary")); + virBufferFreeAndReset(&buf); + return -1; + } + + virBufferAsprintf(&buf, ",dump-guest-core=%s", + virTristateSwitchTypeToString(def->mem.dump_core)); + } + + if (def->mem.nosharepages) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_MERGE)) { + virBufferAddLit(&buf, ",mem-merge=off"); + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("disable shared memory is not available " + "with this QEMU binary")); + virBufferFreeAndReset(&buf); + return -1; + } + } + + if (def->keywrap && + !qemuAppendKeyWrapMachineParms(&buf, qemuCaps, def->keywrap)) { + virBufferFreeAndReset(&buf); + return -1; + } + + if (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON) { + if (def->gic_version != VIR_GIC_VERSION_NONE) { + if ((def->os.arch != VIR_ARCH_ARMV7L && + def->os.arch != VIR_ARCH_AARCH64) || + (STRNEQ(def->os.machine, "virt") && + !STRPREFIX(def->os.machine, "virt-"))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("gic-version option is available " + "only for ARM virt machine")); + virBufferFreeAndReset(&buf); + return -1; + } + + /* 2 is the default, so we don't put it as option for + * backwards compatibility + */ + if (def->gic_version != VIR_GIC_VERSION_2) { + if (!virQEMUCapsGet(qemuCaps, + QEMU_CAPS_MACH_VIRT_GIC_VERSION)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("gic-version option is not available " + "with this QEMU binary")); + virBufferFreeAndReset(&buf); + return -1; + } + + virBufferAsprintf(&buf, ",gic-version=%s", + virGICVersionTypeToString(def->gic_version)); + } + } + } + + virCommandAddArgBuffer(cmd, &buf); + } + + if (obsoleteAccel && + qemuBuildObsoleteAccelArg(cmd, def, qemuCaps) < 0) + return -1; + + return 0; +} + static int qemuBuildObjectCommandLinePropsInternal(const char *key, const virJSONValue *value, @@ -4934,234 +5168,6 @@ qemuBuildCpuArgStr(virQEMUDriverPtr driver, return ret; } -static int -qemuBuildObsoleteAccelArg(virCommandPtr cmd, - const virDomainDef *def, - virQEMUCapsPtr qemuCaps) -{ - bool disableKVM = false; - bool enableKVM = false; - - switch (def->virtType) { - case VIR_DOMAIN_VIRT_QEMU: - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) - disableKVM = true; - break; - - case VIR_DOMAIN_VIRT_KQEMU: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("the QEMU binary does not support kqemu")); - break; - - case VIR_DOMAIN_VIRT_KVM: - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ENABLE_KVM)) { - enableKVM = true; - } else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("the QEMU binary does not support kvm")); - return -1; - } - break; - - case VIR_DOMAIN_VIRT_XEN: - /* XXX better check for xenner */ - break; - - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary does not support %s"), - virDomainVirtTypeToString(def->virtType)); - return -1; - } - - if (disableKVM) - virCommandAddArg(cmd, "-no-kvm"); - if (enableKVM) - virCommandAddArg(cmd, "-enable-kvm"); - - return 0; -} - -static bool -qemuAppendKeyWrapMachineParm(virBuffer *buf, virQEMUCapsPtr qemuCaps, - int flag, const char *pname, int pstate) -{ - if (pstate != VIR_TRISTATE_SWITCH_ABSENT) { - if (!virQEMUCapsGet(qemuCaps, flag)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("%s is not available with this QEMU binary"), pname); - return false; - } - - virBufferAsprintf(buf, ",%s=%s", pname, - virTristateSwitchTypeToString(pstate)); - } - - return true; -} - -static bool -qemuAppendKeyWrapMachineParms(virBuffer *buf, virQEMUCapsPtr qemuCaps, - const virDomainKeyWrapDef *keywrap) -{ - if (!qemuAppendKeyWrapMachineParm(buf, qemuCaps, QEMU_CAPS_AES_KEY_WRAP, - "aes-key-wrap", keywrap->aes)) - return false; - - if (!qemuAppendKeyWrapMachineParm(buf, qemuCaps, QEMU_CAPS_DEA_KEY_WRAP, - "dea-key-wrap", keywrap->dea)) - return false; - - return true; -} - -static int -qemuBuildMachineArgStr(virCommandPtr cmd, - const virDomainDef *def, - virQEMUCapsPtr qemuCaps) -{ - bool obsoleteAccel = false; - - /* This should *never* be NULL, since we always provide - * a machine in the capabilities data for QEMU. So this - * check is just here as a safety in case the unexpected - * happens */ - if (!def->os.machine) - return 0; - - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_OPT)) { - /* if no parameter to the machine type is needed, we still use - * '-M' to keep the most of the compatibility with older versions. - */ - virCommandAddArgList(cmd, "-M", def->os.machine, NULL); - if (def->mem.dump_core) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("dump-guest-core is not available " - "with this QEMU binary")); - return -1; - } - - if (def->mem.nosharepages) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disable shared memory is not available " - "with this QEMU binary")); - return -1; - } - - obsoleteAccel = true; - - if (def->keywrap) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("key wrap support is not available " - "with this QEMU binary")); - return -1; - } - } else { - virBuffer buf = VIR_BUFFER_INITIALIZER; - virTristateSwitch vmport = def->features[VIR_DOMAIN_FEATURE_VMPORT]; - - virCommandAddArg(cmd, "-machine"); - virBufferAdd(&buf, def->os.machine, -1); - - if (def->virtType == VIR_DOMAIN_VIRT_QEMU) - virBufferAddLit(&buf, ",accel=tcg"); - else if (def->virtType == VIR_DOMAIN_VIRT_KVM) - virBufferAddLit(&buf, ",accel=kvm"); - else - obsoleteAccel = true; - - /* To avoid the collision of creating USB controllers when calling - * machine->init in QEMU, it needs to set usb=off - */ - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_USB_OPT)) - virBufferAddLit(&buf, ",usb=off"); - - if (vmport) { - if (!virQEMUCapsSupportsVmport(qemuCaps, def)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("vmport is not available " - "with this QEMU binary")); - virBufferFreeAndReset(&buf); - return -1; - } - - virBufferAsprintf(&buf, ",vmport=%s", - virTristateSwitchTypeToString(vmport)); - } - - if (def->mem.dump_core) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DUMP_GUEST_CORE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("dump-guest-core is not available " - "with this QEMU binary")); - virBufferFreeAndReset(&buf); - return -1; - } - - virBufferAsprintf(&buf, ",dump-guest-core=%s", - virTristateSwitchTypeToString(def->mem.dump_core)); - } - - if (def->mem.nosharepages) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_MERGE)) { - virBufferAddLit(&buf, ",mem-merge=off"); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("disable shared memory is not available " - "with this QEMU binary")); - virBufferFreeAndReset(&buf); - return -1; - } - } - - if (def->keywrap && - !qemuAppendKeyWrapMachineParms(&buf, qemuCaps, def->keywrap)) { - virBufferFreeAndReset(&buf); - return -1; - } - - if (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON) { - if (def->gic_version != VIR_GIC_VERSION_NONE) { - if ((def->os.arch != VIR_ARCH_ARMV7L && - def->os.arch != VIR_ARCH_AARCH64) || - (STRNEQ(def->os.machine, "virt") && - !STRPREFIX(def->os.machine, "virt-"))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("gic-version option is available " - "only for ARM virt machine")); - virBufferFreeAndReset(&buf); - return -1; - } - - /* 2 is the default, so we don't put it as option for - * backwards compatibility - */ - if (def->gic_version != VIR_GIC_VERSION_2) { - if (!virQEMUCapsGet(qemuCaps, - QEMU_CAPS_MACH_VIRT_GIC_VERSION)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("gic-version option is not available " - "with this QEMU binary")); - virBufferFreeAndReset(&buf); - return -1; - } - - virBufferAsprintf(&buf, ",gic-version=%s", - virGICVersionTypeToString(def->gic_version)); - } - } - } - - virCommandAddArgBuffer(cmd, &buf); - } - - if (obsoleteAccel && - qemuBuildObsoleteAccelArg(cmd, def, qemuCaps) < 0) - return -1; - - return 0; -} - static char * qemuBuildSmpArgStr(const virDomainDef *def, virQEMUCapsPtr qemuCaps) @@ -6798,7 +6804,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (enableFips) virCommandAddArg(cmd, "-enable-fips"); - if (qemuBuildMachineArgStr(cmd, def, qemuCaps) < 0) + if (qemuBuildMachineCommandLine(cmd, def, qemuCaps) < 0) goto error; if (qemuBuildCpuArgStr(driver, def, qemuCaps, -- 2.5.0

Reorganize the module to put all the -cpu argument processing code together after the -machine to form a logical order of processing for qemuBuildCommandLine working top down in the module. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 2798 ++++++++++++++++++++++++----------------------- 1 file changed, 1409 insertions(+), 1389 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ede651a..9dbc4a3 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -383,1012 +383,1419 @@ qemuBuildMachineCommandLine(virCommandPtr cmd, return 0; } + +/** Start -cpu arguments */ static int -qemuBuildObjectCommandLinePropsInternal(const char *key, - const virJSONValue *value, - virBufferPtr buf, - bool nested) +qemuBuildCpuModelArgStr(virQEMUDriverPtr driver, + const virDomainDef *def, + virBufferPtr buf, + virQEMUCapsPtr qemuCaps, + bool *hasHwVirt, + bool migrating) { - virJSONValuePtr elem; - virBitmapPtr bitmap = NULL; - ssize_t pos = -1; - ssize_t end; + int ret = -1; size_t i; + virCPUDefPtr host = NULL; + virCPUDefPtr guest = NULL; + virCPUDefPtr cpu = NULL; + virCPUDefPtr featCpu = NULL; + size_t ncpus = 0; + char **cpus = NULL; + virCPUDataPtr data = NULL; + virCPUDataPtr hostData = NULL; + char *compare_msg = NULL; + virCPUCompareResult cmp; + const char *preferred; + virCapsPtr caps = NULL; + bool compareAgainstHost = ((def->virtType == VIR_DOMAIN_VIRT_KVM || + def->cpu->mode != VIR_CPU_MODE_CUSTOM) && + def->cpu->mode != VIR_CPU_MODE_HOST_PASSTHROUGH); - switch ((virJSONType) value->type) { - case VIR_JSON_TYPE_STRING: - virBufferAsprintf(buf, ",%s=%s", key, value->data.string); - break; + if (!(caps = virQEMUDriverGetCapabilities(driver, false))) + goto cleanup; - case VIR_JSON_TYPE_NUMBER: - virBufferAsprintf(buf, ",%s=%s", key, value->data.number); - break; + host = caps->host.cpu; - case VIR_JSON_TYPE_BOOLEAN: - if (value->data.boolean) - virBufferAsprintf(buf, ",%s=yes", key); - else - virBufferAsprintf(buf, ",%s=no", key); + if (!host || + !host->model || + (ncpus = virQEMUCapsGetCPUDefinitions(qemuCaps, &cpus)) == 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("CPU specification not supported by hypervisor")); + goto cleanup; + } - break; + if (!(cpu = virCPUDefCopy(def->cpu))) + goto cleanup; - case VIR_JSON_TYPE_ARRAY: - if (nested) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("nested -object property arrays are not supported")); - return -1; - } + if (cpu->mode == VIR_CPU_MODE_HOST_MODEL && + !migrating && + cpuUpdate(cpu, host) < 0) + goto cleanup; - if (virJSONValueGetArrayAsBitmap(value, &bitmap) == 0) { - while ((pos = virBitmapNextSetBit(bitmap, pos)) > -1) { - if ((end = virBitmapNextClearBit(bitmap, pos)) < 0) - end = virBitmapLastSetBit(bitmap) + 1; + /* For non-KVM, CPU features are emulated, so host compat doesn't matter */ + if (compareAgainstHost) { + bool noTSX = false; - if (end - 1 > pos) { - virBufferAsprintf(buf, ",%s=%zd-%zd", key, pos, end - 1); - pos = end; + cmp = cpuGuestData(host, cpu, &data, &compare_msg); + switch (cmp) { + case VIR_CPU_COMPARE_INCOMPATIBLE: + if (cpuEncode(host->arch, host, NULL, &hostData, + NULL, NULL, NULL, NULL) == 0 && + (!cpuHasFeature(hostData, "hle") || + !cpuHasFeature(hostData, "rtm")) && + (STREQ_NULLABLE(cpu->model, "Haswell") || + STREQ_NULLABLE(cpu->model, "Broadwell"))) + noTSX = true; + + if (compare_msg) { + if (noTSX) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("guest and host CPU are not compatible: " + "%s; try using '%s-noTSX' CPU model"), + compare_msg, cpu->model); } else { - virBufferAsprintf(buf, ",%s=%zd", key, pos); + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("guest and host CPU are not compatible: " + "%s"), + compare_msg); + } + } else { + if (noTSX) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("guest CPU is not compatible with host " + "CPU; try using '%s-noTSX' CPU model"), + cpu->model); + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("guest CPU is not compatible with host " + "CPU")); } } - } else { - /* fallback, treat the array as a non-bitmap, adding the key - * for each member */ - for (i = 0; i < virJSONValueArraySize(value); i++) { - elem = virJSONValueArrayGet((virJSONValuePtr)value, i); + /* fall through */ + case VIR_CPU_COMPARE_ERROR: + goto cleanup; - /* recurse to avoid duplicating code */ - if (qemuBuildObjectCommandLinePropsInternal(key, elem, buf, - true) < 0) - return -1; - } + default: + break; } - break; + } - case VIR_JSON_TYPE_OBJECT: - case VIR_JSON_TYPE_NULL: - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("NULL and OBJECT JSON types can't be converted to " - "commandline string")); - return -1; + /* Only 'svm' requires --enable-nesting. The nested + * 'vmx' patches now simply hook off the CPU features + */ + if ((def->os.arch == VIR_ARCH_X86_64 || def->os.arch == VIR_ARCH_I686) && + compareAgainstHost) { + int hasSVM = cpuHasFeature(data, "svm"); + if (hasSVM < 0) + goto cleanup; + *hasHwVirt = hasSVM > 0 ? true : false; } - virBitmapFree(bitmap); - return 0; -} + if ((cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) || + ((cpu->mode == VIR_CPU_MODE_HOST_MODEL) && + ARCH_IS_PPC64(def->os.arch))) { + const char *mode = virCPUModeTypeToString(cpu->mode); + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_HOST)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("CPU mode '%s' is not supported by QEMU" + " binary"), mode); + goto cleanup; + } + if (def->virtType != VIR_DOMAIN_VIRT_KVM) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("CPU mode '%s' is only supported with kvm"), + mode); + goto cleanup; + } + virBufferAddLit(buf, "host"); + if (def->os.arch == VIR_ARCH_ARMV7L && + host->arch == VIR_ARCH_AARCH64) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_AARCH64_OFF)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("QEMU binary does not support CPU " + "host-passthrough for armv7l on " + "aarch64 host")); + goto cleanup; + } -static int -qemuBuildObjectCommandLineProps(const char *key, - const virJSONValue *value, - void *opaque) -{ - return qemuBuildObjectCommandLinePropsInternal(key, value, opaque, false); -} + virBufferAddLit(buf, ",aarch64=off"); + } + if (ARCH_IS_PPC64(def->os.arch) && + cpu->mode == VIR_CPU_MODE_HOST_MODEL && + def->cpu->model != NULL) { + virBufferAsprintf(buf, ",compat=%s", def->cpu->model); + } else { + featCpu = cpu; + } -char * -qemuBuildObjectCommandlineFromJSON(const char *type, - const char *alias, - virJSONValuePtr props) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *ret = NULL; + } else { + if (VIR_ALLOC(guest) < 0) + goto cleanup; + if (VIR_STRDUP(guest->vendor_id, cpu->vendor_id) < 0) + goto cleanup; - virBufferAsprintf(&buf, "%s,id=%s", type, alias); + if (compareAgainstHost) { + guest->arch = host->arch; + if (cpu->match == VIR_CPU_MATCH_MINIMUM) + preferred = host->model; + else + preferred = cpu->model; - if (virJSONValueObjectForeachKeyValue(props, - qemuBuildObjectCommandLineProps, - &buf) < 0) - goto cleanup; + guest->type = VIR_CPU_TYPE_GUEST; + guest->fallback = cpu->fallback; + if (cpuDecode(guest, data, + (const char **)cpus, ncpus, preferred) < 0) + goto cleanup; + } else { + guest->arch = def->os.arch; + if (VIR_STRDUP(guest->model, cpu->model) < 0) + goto cleanup; + } + virBufferAdd(buf, guest->model, -1); + if (guest->vendor_id) + virBufferAsprintf(buf, ",vendor=%s", guest->vendor_id); + featCpu = guest; + } - if (virBufferCheckError(&buf) < 0) - goto cleanup; + if (featCpu) { + for (i = 0; i < featCpu->nfeatures; i++) { + char sign; + if (featCpu->features[i].policy == VIR_CPU_FEATURE_DISABLE) + sign = '-'; + else + sign = '+'; - ret = virBufferContentAndReset(&buf); + virBufferAsprintf(buf, ",%c%s", sign, featCpu->features[i].name); + } + } + ret = 0; cleanup: - virBufferFreeAndReset(&buf); + virObjectUnref(caps); + VIR_FREE(compare_msg); + cpuDataFree(data); + cpuDataFree(hostData); + virCPUDefFree(guest); + virCPUDefFree(cpu); return ret; } static int -qemuBuildDeviceAddressStr(virBufferPtr buf, - virDomainDefPtr domainDef, - virDomainDeviceInfoPtr info, - virQEMUCapsPtr qemuCaps) +qemuBuildCpuArgStr(virQEMUDriverPtr driver, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps, + char **opt, + bool *hasHwVirt, + bool migrating) { - int ret = -1; - char *devStr = NULL; - const char *contAlias = NULL; + virArch hostarch = virArchFromHost(); + const char *default_model; + bool have_cpu = false; + int ret = -1; + virBuffer buf = VIR_BUFFER_INITIALIZER; + size_t i; - if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - size_t i; + *hasHwVirt = false; - if (!(devStr = virDomainPCIAddressAsString(&info->addr.pci))) - goto cleanup; - for (i = 0; i < domainDef->ncontrollers; i++) { - virDomainControllerDefPtr cont = domainDef->controllers[i]; + if (def->os.arch == VIR_ARCH_I686) + default_model = "qemu32"; + else + default_model = "qemu64"; - 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); + if (def->cpu && + (def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) { + if (qemuBuildCpuModelArgStr(driver, def, &buf, qemuCaps, + hasHwVirt, migrating) < 0) goto cleanup; + have_cpu = true; + } else { + /* + * Need to force a 32-bit guest CPU type if + * + * 1. guest OS is i686 + * 2. host OS is x86_64 + * 3. emulator is qemu-kvm or kvm + * + * Or + * + * 1. guest OS is i686 + * 2. emulator is qemu-system-x86_64 + */ + if (def->os.arch == VIR_ARCH_I686 && + ((hostarch == VIR_ARCH_X86_64 && + strstr(def->emulator, "kvm")) || + strstr(def->emulator, "x86_64"))) { + virBufferAdd(&buf, default_model, -1); + have_cpu = true; } + } - 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; - } - } + /* Handle paravirtual timers */ + for (i = 0; i < def->clock.ntimers; i++) { + virDomainTimerDefPtr timer = def->clock.timers[i]; - 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; + if (timer->present == -1) + continue; + + if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK) { + virBufferAsprintf(&buf, "%s,%ckvmclock", + have_cpu ? "" : default_model, + timer->present ? '+' : '-'); + have_cpu = true; + } else if (timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK && + timer->present) { + virBufferAsprintf(&buf, "%s,hv_time", + have_cpu ? "" : default_model); + have_cpu = true; } - 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))) - 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); + if (def->apic_eoi) { + char sign; + if (def->apic_eoi == VIR_TRISTATE_SWITCH_ON) + sign = '+'; + else + sign = '-'; + + virBufferAsprintf(&buf, "%s,%ckvm_pv_eoi", + have_cpu ? "" : default_model, + sign); + have_cpu = true; } - ret = 0; - cleanup: - VIR_FREE(devStr); - return ret; -} + if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK]) { + char sign; + if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK] == VIR_TRISTATE_SWITCH_ON) + sign = '+'; + else + sign = '-'; -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; + virBufferAsprintf(&buf, "%s,%ckvm_pv_unhalt", + have_cpu ? "" : default_model, + sign); + have_cpu = true; + } + + if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) { + if (!have_cpu) { + virBufferAdd(&buf, default_model, -1); + have_cpu = true; } - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_ROMBAR)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("rombar and romfile not supported in this QEMU binary")); - return -1; + + for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { + switch ((virDomainHyperv) i) { + case VIR_DOMAIN_HYPERV_RELAXED: + case VIR_DOMAIN_HYPERV_VAPIC: + if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON) + virBufferAsprintf(&buf, ",hv_%s", + virDomainHypervTypeToString(i)); + break; + + case VIR_DOMAIN_HYPERV_SPINLOCKS: + if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON) + virBufferAsprintf(&buf, ",hv_spinlocks=0x%x", + def->hyperv_spinlocks); + break; + + /* coverity[dead_error_begin] */ + case VIR_DOMAIN_HYPERV_LAST: + break; + } } + } - switch (info->rombar) { - case VIR_TRISTATE_SWITCH_OFF: - virBufferAddLit(buf, ",rombar=0"); - break; - case VIR_TRISTATE_SWITCH_ON: - virBufferAddLit(buf, ",rombar=1"); - break; - default: + for (i = 0; i < def->npanics; i++) { + if (def->panics[i]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV) { + if (!have_cpu) { + virBufferAdd(&buf, default_model, -1); + have_cpu = true; + } + + virBufferAddLit(&buf, ",hv_crash"); break; } - if (info->romfile) - virBufferAsprintf(buf, ",romfile=%s", info->romfile); } - 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; -} + if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) { + if (!have_cpu) { + virBufferAdd(&buf, default_model, -1); + have_cpu = true; + } -#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ " + for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) { + switch ((virDomainKVM) i) { + case VIR_DOMAIN_KVM_HIDDEN: + if (def->kvm_features[i] == VIR_TRISTATE_SWITCH_ON) + virBufferAddLit(&buf, ",kvm=off"); + break; -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; + /* coverity[dead_error_begin] */ + case VIR_DOMAIN_KVM_LAST: + break; + } + } } - return 0; -} - -static char * -qemuGetSecretString(virConnectPtr conn, - const char *scheme, - bool encoded, - virStorageAuthDefPtr authdef, - virSecretUsageType secretUsageType) -{ - 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; - } + if (def->features[VIR_DOMAIN_FEATURE_PMU]) { + virTristateSwitch pmu = def->features[VIR_DOMAIN_FEATURE_PMU]; + if (!have_cpu) + virBufferAdd(&buf, default_model, -1); - 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; + virBufferAsprintf(&buf, ",pmu=%s", + virTristateSwitchTypeToString(pmu)); + have_cpu = true; } - 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 (virBufferCheckError(&buf) < 0) goto cleanup; - } - if (encoded) { - char *base64 = NULL; + *opt = virBufferContentAndReset(&buf); - base64_encode_alloc(secret, secret_size, &base64); - VIR_FREE(secret); - if (!base64) { - virReportOOMError(); - goto cleanup; - } - secret = base64; - } + ret = 0; cleanup: - virObjectUnref(sec); - return secret; + return ret; } static int -qemuNetworkDriveGetPort(int protocol, - const char *port) +qemuBuildCpuCommandLine(virCommandPtr cmd, + virQEMUDriverPtr driver, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps, + bool migrating) { - int ret = 0; + char *cpu; + bool hasHwVirt = false; - 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; - } + if (qemuBuildCpuArgStr(driver, def, qemuCaps, + &cpu, &hasHwVirt, migrating) < 0) + goto error; - return ret; + if (cpu) { + virCommandAddArgList(cmd, "-cpu", cpu, NULL); + VIR_FREE(cpu); + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NESTING) && hasHwVirt) + virCommandAddArg(cmd, "-enable-nesting"); } - switch ((virStorageNetProtocol) protocol) { - case VIR_STORAGE_NET_PROTOCOL_HTTP: - return 80; + return 0; - case VIR_STORAGE_NET_PROTOCOL_HTTPS: - return 443; + error: + return -1; +} - case VIR_STORAGE_NET_PROTOCOL_FTP: - return 21; - case VIR_STORAGE_NET_PROTOCOL_FTPS: - return 990; +static int +qemuBuildObjectCommandLinePropsInternal(const char *key, + const virJSONValue *value, + virBufferPtr buf, + bool nested) +{ + virJSONValuePtr elem; + virBitmapPtr bitmap = NULL; + ssize_t pos = -1; + ssize_t end; + size_t i; - case VIR_STORAGE_NET_PROTOCOL_TFTP: - return 69; + switch ((virJSONType) value->type) { + case VIR_JSON_TYPE_STRING: + virBufferAsprintf(buf, ",%s=%s", key, value->data.string); + break; - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - return 7000; + case VIR_JSON_TYPE_NUMBER: + virBufferAsprintf(buf, ",%s=%s", key, value->data.number); + break; - case VIR_STORAGE_NET_PROTOCOL_NBD: - return 10809; + case VIR_JSON_TYPE_BOOLEAN: + if (value->data.boolean) + virBufferAsprintf(buf, ",%s=yes", key); + else + virBufferAsprintf(buf, ",%s=no", key); - case VIR_STORAGE_NET_PROTOCOL_ISCSI: - case VIR_STORAGE_NET_PROTOCOL_GLUSTER: - /* no default port specified */ - return 0; + break; - case VIR_STORAGE_NET_PROTOCOL_RBD: - case VIR_STORAGE_NET_PROTOCOL_LAST: - case VIR_STORAGE_NET_PROTOCOL_NONE: - /* not applicable */ + case VIR_JSON_TYPE_ARRAY: + if (nested) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("nested -object property arrays are not supported")); return -1; + } + + if (virJSONValueGetArrayAsBitmap(value, &bitmap) == 0) { + while ((pos = virBitmapNextSetBit(bitmap, pos)) > -1) { + if ((end = virBitmapNextClearBit(bitmap, pos)) < 0) + end = virBitmapLastSetBit(bitmap) + 1; + + if (end - 1 > pos) { + virBufferAsprintf(buf, ",%s=%zd-%zd", key, pos, end - 1); + pos = end; + } else { + virBufferAsprintf(buf, ",%s=%zd", key, pos); + } + } + } else { + /* fallback, treat the array as a non-bitmap, adding the key + * for each member */ + for (i = 0; i < virJSONValueArraySize(value); i++) { + elem = virJSONValueArrayGet((virJSONValuePtr)value, i); + + /* recurse to avoid duplicating code */ + if (qemuBuildObjectCommandLinePropsInternal(key, elem, buf, + true) < 0) + return -1; + } + } + break; + + case VIR_JSON_TYPE_OBJECT: + case VIR_JSON_TYPE_NULL: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("NULL and OBJECT JSON types can't be converted to " + "commandline string")); + return -1; } - return -1; + virBitmapFree(bitmap); + return 0; } -#define QEMU_DEFAULT_NBD_PORT "10809" -static char * -qemuBuildNetworkDriveURI(virStorageSourcePtr src, - const char *username, - const char *secret) +static int +qemuBuildObjectCommandLineProps(const char *key, + const virJSONValue *value, + void *opaque) { - char *ret = NULL; - virBuffer buf = VIR_BUFFER_INITIALIZER; - virURIPtr uri = NULL; - size_t i; + return qemuBuildObjectCommandLinePropsInternal(key, value, opaque, false); +} - 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] != '/'))) { +char * +qemuBuildObjectCommandlineFromJSON(const char *type, + const char *alias, + virJSONValuePtr props) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *ret = NULL; - virBufferAddLit(&buf, "nbd:"); + virBufferAsprintf(&buf, "%s,id=%s", type, alias); - 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 (virJSONValueObjectForeachKeyValue(props, + qemuBuildObjectCommandLineProps, + &buf) < 0) + 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; - } + if (virBufferCheckError(&buf) < 0) + goto cleanup; - virBufferAsprintf(&buf, "unix:%s", src->hosts->socket); - break; + ret = virBufferContentAndReset(&buf); - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("nbd does not support transport '%s'"), - virStorageNetHostTransportTypeToString(src->hosts->transport)); - goto cleanup; - } + cleanup: + virBufferFreeAndReset(&buf); + return ret; +} - if (src->path) - virBufferAsprintf(&buf, ":exportname=%s", src->path); - - if (virBufferCheckError(&buf) < 0) - goto cleanup; - ret = virBufferContentAndReset(&buf); - goto cleanup; - } - /* fallthrough */ - /* NBD code uses same formatting scheme as others in some cases */ +static int +qemuBuildDeviceAddressStr(virBufferPtr buf, + virDomainDefPtr domainDef, + virDomainDeviceInfoPtr info, + virQEMUCapsPtr qemuCaps) +{ + int ret = -1; + char *devStr = NULL; + const char *contAlias = NULL; - 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; - } + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + size_t i; - if (VIR_ALLOC(uri) < 0) - goto cleanup; + if (!(devStr = virDomainPCIAddressAsString(&info->addr.pci))) + goto cleanup; + for (i = 0; i < domainDef->ncontrollers; i++) { + virDomainControllerDefPtr cont = domainDef->controllers[i]; - 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) + 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; - } - - if ((uri->port = qemuNetworkDriveGetPort(src->protocol, src->hosts->port)) < 0) - 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 (src->hosts->socket && - virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0) - goto cleanup; - - 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; } + 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 (VIR_STRDUP(uri->server, src->hosts->name) < 0) - goto cleanup; - - ret = virURIFormat(uri); - - break; - - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - if (!src->path) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing disk source for 'sheepdog' protocol")); - goto cleanup; - } - - 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")); + 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; } - - break; - - 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); + 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; } + } - virBufferStrcat(&buf, "rbd:", src->path, NULL); - - 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 (src->configFile) - virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile); - - if (virBufferCheckError(&buf) < 0) - goto cleanup; - - ret = virBufferContentAndReset(&buf); - break; - + 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); - case VIR_STORAGE_NET_PROTOCOL_LAST: - case VIR_STORAGE_NET_PROTOCOL_NONE: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unexpected network protocol '%s'"), - virStorageNetProtocolTypeToString(src->protocol)); + 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; + 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: - virBufferFreeAndReset(&buf); - virURIFree(uri); - + VIR_FREE(devStr); return ret; } - -int -qemuGetDriveSourceString(virStorageSourcePtr src, - virConnectPtr conn, - char **source) +static int +qemuBuildRomStr(virBufferPtr buf, + virDomainDeviceInfoPtr info, + virQEMUCapsPtr qemuCaps) { - int actualType = virStorageSourceGetActualType(src); - char *secret = NULL; - char *username = NULL; - int ret = -1; - - *source = NULL; - - /* return 1 for empty sources */ - if (virStorageSourceIsEmpty(src)) - return 1; + 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 (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 (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 (!(secret = qemuGetSecretString(conn, - protocol, - encode, - src->auth, - secretType))) - 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); } + return 0; +} - 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 int +qemuBuildIoEventFdStr(virBufferPtr buf, + virTristateSwitch use, + virQEMUCapsPtr qemuCaps) +{ + if (use && virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_IOEVENTFD)) + virBufferAsprintf(buf, ",ioeventfd=%s", + virTristateSwitchTypeToString(use)); + return 0; +} - case VIR_STORAGE_TYPE_NETWORK: - if (!(*source = qemuBuildNetworkDriveURI(src, username, secret))) - goto cleanup; - break; +#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ " - case VIR_STORAGE_TYPE_VOLUME: - case VIR_STORAGE_TYPE_NONE: - case VIR_STORAGE_TYPE_LAST: - break; +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; } - ret = 0; - - cleanup: - VIR_FREE(secret); - return ret; + return 0; } - -/* Perform disk definition config validity checks */ -int -qemuCheckDiskConfig(virDomainDiskDefPtr disk) +static char * +qemuGetSecretString(virConnectPtr conn, + const char *scheme, + bool encoded, + virStorageAuthDefPtr authdef, + virSecretUsageType secretUsageType) { - if (virDiskNameToIndex(disk->dst) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } + size_t secret_size; + virSecretPtr sec = NULL; + char *secret = NULL; + char uuidStr[VIR_UUID_STRING_BUFLEN]; - 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; - } + /* 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 ((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; + 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; } - 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; + 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; } - return 0; - error: - return -1; -} + if (encoded) { + char *base64 = NULL; -/* 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; + base64_encode_alloc(secret, secret_size, &base64); + VIR_FREE(secret); + if (!base64) { + virReportOOMError(); + goto cleanup; } + secret = base64; } - return true; + + cleanup: + virObjectUnref(sec); + return secret; } -/* 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) +static int +qemuNetworkDriveGetPort(int protocol, + const char *port) { - bool ret = false; + int ret = 0; - if (virFileExists("/proc/sys/crypto/fips_enabled")) { - char *buf = NULL; + 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; + } - if (virFileReadAll("/proc/sys/crypto/fips_enabled", 10, &buf) < 0) - return ret; - if (STREQ(buf, "1\n")) - ret = true; - VIR_FREE(buf); + return ret; } - return ret; -} - - -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); + switch ((virStorageNetProtocol) protocol) { + case VIR_STORAGE_NET_PROTOCOL_HTTP: + return 80; - if (idx < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + return 443; - 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; - } + case VIR_STORAGE_NET_PROTOCOL_FTP: + return 21; - /* 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; + case VIR_STORAGE_NET_PROTOCOL_FTPS: + return 990; - 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) { - 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_STORAGE_NET_PROTOCOL_TFTP: + return 69; - 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; + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + return 7000; - break; + case VIR_STORAGE_NET_PROTOCOL_NBD: + return 10809; - case VIR_DOMAIN_DISK_BUS_VIRTIO: - idx = -1; - break; + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + /* no default port specified */ + return 0; - 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_RBD: + case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: + /* not applicable */ + return -1; } - if (qemuGetDriveSourceString(disk->src, conn, &source) < 0) - goto error; + return -1; +} - if (source && - !((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || - disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) && - disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { +#define QEMU_DEFAULT_NBD_PORT "10809" - virBufferAddLit(&opt, "file="); +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 (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) { + switch ((virStorageNetProtocol) src->protocol) { + case VIR_STORAGE_NET_PROTOCOL_NBD: + if (src->nhosts != 1) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk driver type for '%s'"), - virStorageFileFormatTypeToString(disk->src->format)); - goto error; + _("protocol '%s' accepts only one host"), + virStorageNetProtocolTypeToString(src->protocol)); + goto cleanup; } - if (!disk->src->readonly) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot create virtual FAT disks in read-write mode")); - goto error; - } + 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] != '/'))) { - virBufferAddLit(&opt, "fat:"); + virBufferAddLit(&buf, "nbd:"); - if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) - virBufferAddLit(&opt, "floppy:"); + 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; - break; + 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; + } - 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; - } + virBufferAsprintf(&buf, "unix:%s", src->hosts->socket); + break; - break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("nbd does not support transport '%s'"), + virStorageNetHostTransportTypeToString(src->hosts->transport)); + goto cleanup; + } - default: - break; - } + if (src->path) + virBufferAsprintf(&buf, ":exportname=%s", src->path); - virBufferEscape(&opt, ',', ",", "%s,", source); + if (virBufferCheckError(&buf) < 0) + goto cleanup; - if (disk->src->format > 0 && - disk->src->type != VIR_STORAGE_TYPE_DIR) - virBufferAsprintf(&opt, "format=%s,", - virStorageFileFormatTypeToString(disk->src->format)); - } - VIR_FREE(source); + ret = virBufferContentAndReset(&buf); + goto cleanup; + } + /* fallthrough */ + /* NBD code uses same formatting scheme as others in some cases */ - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) - virBufferAddLit(&opt, "if=none"); - else - virBufferAsprintf(&opt, "if=%s", bus); + 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; + } - 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 (VIR_ALLOC(uri) < 0) + 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 || + 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 ((uri->port = qemuNetworkDriveGetPort(src->protocol, src->hosts->port)) < 0) + 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 (src->hosts->socket && + virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0) + goto cleanup; + + 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 (VIR_STRDUP(uri->server, src->hosts->name) < 0) + goto cleanup; + + ret = virURIFormat(uri); + + break; + + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + if (!src->path) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing disk source for 'sheepdog' protocol")); + goto cleanup; + } + + 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; + } + + break; + + 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); + + 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 (src->configFile) + virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + ret = virBufferContentAndReset(&buf); + break; + + + 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; + } + + cleanup: + virBufferFreeAndReset(&buf); + virURIFree(uri); + + return ret; +} + + +int +qemuGetDriveSourceString(virStorageSourcePtr src, + virConnectPtr conn, + char **source) +{ + int actualType = virStorageSourceGetActualType(src); + char *secret = NULL; + char *username = NULL; + int ret = -1; + + *source = NULL; + + /* return 1 for empty sources */ + if (virStorageSourceIsEmpty(src)) + return 1; + + 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 (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 (!(secret = qemuGetSecretString(conn, + protocol, + encode, + src->auth, + secretType))) + 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) + goto cleanup; + + break; + + case VIR_STORAGE_TYPE_NETWORK: + if (!(*source = qemuBuildNetworkDriveURI(src, username, secret))) + goto cleanup; + break; + + case VIR_STORAGE_TYPE_VOLUME: + case VIR_STORAGE_TYPE_NONE: + case VIR_STORAGE_TYPE_LAST: + break; + } + + ret = 0; + + cleanup: + VIR_FREE(secret); + return ret; +} + + +/* Perform disk definition config validity checks */ +int +qemuCheckDiskConfig(virDomainDiskDefPtr disk) +{ + if (virDiskNameToIndex(disk->dst) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), disk->dst); + goto error; + } + + 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; + } + } + + 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; + } + + 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; + } + } + 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) +{ + 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; +} + + +/* 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; + + if (virFileExists("/proc/sys/crypto/fips_enabled")) { + char *buf = NULL; + + if (virFileReadAll("/proc/sys/crypto/fips_enabled", 10, &buf) < 0) + return ret; + if (STREQ(buf, "1\n")) + ret = true; + VIR_FREE(buf); + } + + return ret; +} + + +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; + } + + /* 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; + + 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) { + 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_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; + + break; + + case VIR_DOMAIN_DISK_BUS_VIRTIO: + idx = -1; + break; + + 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 (qemuGetDriveSourceString(disk->src, conn, &source) < 0) + goto error; + + if (source && + !((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || + disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) && + disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { + + virBufferAddLit(&opt, "file="); + + 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 (!disk->src->readonly) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create virtual FAT disks in read-write mode")); + goto error; + } + + virBufferAddLit(&opt, "fat:"); + + if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) + virBufferAddLit(&opt, "floppy:"); + + break; + + 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; + } + + 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 (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"); @@ -4569,220 +4976,17 @@ static char *qemuBuildSmbiosBiosStr(virSysinfoBIOSDefPtr def) virBufferAddLit(&buf, "type=0"); /* 0:Vendor */ - if (def->vendor) - virBufferAsprintf(&buf, ",vendor=%s", def->vendor); - /* 0:BIOS Version */ - if (def->version) - virBufferAsprintf(&buf, ",version=%s", def->version); - /* 0:BIOS Release Date */ - if (def->date) - virBufferAsprintf(&buf, ",date=%s", def->date); - /* 0:System BIOS Major Release and 0:System BIOS Minor Release */ - if (def->release) - virBufferAsprintf(&buf, ",release=%s", def->release); - - if (virBufferCheckError(&buf) < 0) - goto error; - - return virBufferContentAndReset(&buf); - - error: - virBufferFreeAndReset(&buf); - return NULL; -} - -static char *qemuBuildSmbiosSystemStr(virSysinfoSystemDefPtr def, - bool skip_uuid) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if (!def || - (!def->manufacturer && !def->product && !def->version && - !def->serial && (!def->uuid || skip_uuid) && - def->sku && !def->family)) - return NULL; - - virBufferAddLit(&buf, "type=1"); - - /* 1:Manufacturer */ - if (def->manufacturer) - virBufferAsprintf(&buf, ",manufacturer=%s", - def->manufacturer); - /* 1:Product Name */ - if (def->product) - virBufferAsprintf(&buf, ",product=%s", def->product); - /* 1:Version */ - if (def->version) - virBufferAsprintf(&buf, ",version=%s", def->version); - /* 1:Serial Number */ - if (def->serial) - virBufferAsprintf(&buf, ",serial=%s", def->serial); - /* 1:UUID */ - if (def->uuid && !skip_uuid) - virBufferAsprintf(&buf, ",uuid=%s", def->uuid); - /* 1:SKU Number */ - if (def->sku) - virBufferAsprintf(&buf, ",sku=%s", def->sku); - /* 1:Family */ - if (def->family) - virBufferAsprintf(&buf, ",family=%s", def->family); - - if (virBufferCheckError(&buf) < 0) - goto error; - - return virBufferContentAndReset(&buf); - - error: - virBufferFreeAndReset(&buf); - return NULL; -} - -static char *qemuBuildSmbiosBaseBoardStr(virSysinfoBaseBoardDefPtr def) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if (!def) - return NULL; - - virBufferAddLit(&buf, "type=2"); - - /* 2:Manufacturer */ - if (def->manufacturer) - virBufferAsprintf(&buf, ",manufacturer=%s", - def->manufacturer); - /* 2:Product Name */ - if (def->product) - virBufferAsprintf(&buf, ",product=%s", def->product); - /* 2:Version */ - if (def->version) - virBufferAsprintf(&buf, ",version=%s", def->version); - /* 2:Serial Number */ - if (def->serial) - virBufferAsprintf(&buf, ",serial=%s", def->serial); - /* 2:Asset Tag */ - if (def->asset) - virBufferAsprintf(&buf, ",asset=%s", def->asset); - /* 2:Location */ - if (def->location) - virBufferAsprintf(&buf, ",location=%s", def->location); - - if (virBufferCheckError(&buf) < 0) - goto error; - - return virBufferContentAndReset(&buf); - - error: - virBufferFreeAndReset(&buf); - return NULL; -} - -static char * -qemuBuildClockArgStr(virDomainClockDefPtr def) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - switch (def->offset) { - case VIR_DOMAIN_CLOCK_OFFSET_UTC: - virBufferAddLit(&buf, "base=utc"); - break; - - case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: - case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: - virBufferAddLit(&buf, "base=localtime"); - break; - - case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: { - time_t now = time(NULL); - struct tm nowbits; - - if (def->data.variable.basis == VIR_DOMAIN_CLOCK_BASIS_LOCALTIME) { - long localOffset; - - /* in the case of basis='localtime', rather than trying to - * keep that basis (and associated offset from UTC) in the - * status and deal with adding in the difference each time - * there is an RTC_CHANGE event, it is simpler and less - * error prone to just convert the adjustment an offset - * from UTC right now (and change the status to - * "basis='utc' to reflect this). This eliminates - * potential errors in both RTC_CHANGE events and in - * migration (in the case that the status of DST, or the - * timezone of the destination host, changed relative to - * startup). - */ - if (virTimeLocalOffsetFromUTC(&localOffset) < 0) - goto error; - def->data.variable.adjustment += localOffset; - def->data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC; - } - - now += def->data.variable.adjustment; - gmtime_r(&now, &nowbits); - - /* when an RTC_CHANGE event is received from qemu, we need to - * have the adjustment used at domain start time available to - * compute the new offset from UTC. As this new value is - * itself stored in def->data.variable.adjustment, we need to - * save a copy of it now. - */ - def->data.variable.adjustment0 = def->data.variable.adjustment; - - virBufferAsprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d", - nowbits.tm_year + 1900, - nowbits.tm_mon + 1, - nowbits.tm_mday, - nowbits.tm_hour, - nowbits.tm_min, - nowbits.tm_sec); - } break; - - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported clock offset '%s'"), - virDomainClockOffsetTypeToString(def->offset)); - goto error; - } - - /* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */ - size_t i; - for (i = 0; i < def->ntimers; i++) { - if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) { - switch (def->timers[i]->track) { - case -1: /* unspecified - use hypervisor default */ - break; - case VIR_DOMAIN_TIMER_TRACK_BOOT: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc timer track '%s'"), - virDomainTimerTrackTypeToString(def->timers[i]->track)); - goto error; - case VIR_DOMAIN_TIMER_TRACK_GUEST: - virBufferAddLit(&buf, ",clock=vm"); - break; - case VIR_DOMAIN_TIMER_TRACK_WALL: - virBufferAddLit(&buf, ",clock=host"); - break; - } - - switch (def->timers[i]->tickpolicy) { - case -1: - case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: - /* This is the default - missed ticks delivered when - next scheduled, at normal rate */ - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - /* deliver ticks at a faster rate until caught up */ - virBufferAddLit(&buf, ",driftfix=slew"); - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: - case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc timer tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy)); - goto error; - } - break; /* no need to check other timers - there is only one rtc */ - } - } + if (def->vendor) + virBufferAsprintf(&buf, ",vendor=%s", def->vendor); + /* 0:BIOS Version */ + if (def->version) + virBufferAsprintf(&buf, ",version=%s", def->version); + /* 0:BIOS Release Date */ + if (def->date) + virBufferAsprintf(&buf, ",date=%s", def->date); + /* 0:System BIOS Major Release and 0:System BIOS Minor Release */ + if (def->release) + virBufferAsprintf(&buf, ",release=%s", def->release); if (virBufferCheckError(&buf) < 0) goto error; @@ -4794,378 +4998,207 @@ qemuBuildClockArgStr(virDomainClockDefPtr def) return NULL; } -static int -qemuBuildCpuModelArgStr(virQEMUDriverPtr driver, - const virDomainDef *def, - virBufferPtr buf, - virQEMUCapsPtr qemuCaps, - bool *hasHwVirt, - bool migrating) +static char *qemuBuildSmbiosSystemStr(virSysinfoSystemDefPtr def, + bool skip_uuid) { - int ret = -1; - size_t i; - virCPUDefPtr host = NULL; - virCPUDefPtr guest = NULL; - virCPUDefPtr cpu = NULL; - virCPUDefPtr featCpu = NULL; - size_t ncpus = 0; - char **cpus = NULL; - virCPUDataPtr data = NULL; - virCPUDataPtr hostData = NULL; - char *compare_msg = NULL; - virCPUCompareResult cmp; - const char *preferred; - virCapsPtr caps = NULL; - bool compareAgainstHost = ((def->virtType == VIR_DOMAIN_VIRT_KVM || - def->cpu->mode != VIR_CPU_MODE_CUSTOM) && - def->cpu->mode != VIR_CPU_MODE_HOST_PASSTHROUGH); - - if (!(caps = virQEMUDriverGetCapabilities(driver, false))) - goto cleanup; - - host = caps->host.cpu; - - if (!host || - !host->model || - (ncpus = virQEMUCapsGetCPUDefinitions(qemuCaps, &cpus)) == 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("CPU specification not supported by hypervisor")); - goto cleanup; - } - - if (!(cpu = virCPUDefCopy(def->cpu))) - goto cleanup; - - if (cpu->mode == VIR_CPU_MODE_HOST_MODEL && - !migrating && - cpuUpdate(cpu, host) < 0) - goto cleanup; - - /* For non-KVM, CPU features are emulated, so host compat doesn't matter */ - if (compareAgainstHost) { - bool noTSX = false; - - cmp = cpuGuestData(host, cpu, &data, &compare_msg); - switch (cmp) { - case VIR_CPU_COMPARE_INCOMPATIBLE: - if (cpuEncode(host->arch, host, NULL, &hostData, - NULL, NULL, NULL, NULL) == 0 && - (!cpuHasFeature(hostData, "hle") || - !cpuHasFeature(hostData, "rtm")) && - (STREQ_NULLABLE(cpu->model, "Haswell") || - STREQ_NULLABLE(cpu->model, "Broadwell"))) - noTSX = true; - - if (compare_msg) { - if (noTSX) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("guest and host CPU are not compatible: " - "%s; try using '%s-noTSX' CPU model"), - compare_msg, cpu->model); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("guest and host CPU are not compatible: " - "%s"), - compare_msg); - } - } else { - if (noTSX) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("guest CPU is not compatible with host " - "CPU; try using '%s-noTSX' CPU model"), - cpu->model); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("guest CPU is not compatible with host " - "CPU")); - } - } - /* fall through */ - case VIR_CPU_COMPARE_ERROR: - goto cleanup; - - default: - break; - } - } - - /* Only 'svm' requires --enable-nesting. The nested - * 'vmx' patches now simply hook off the CPU features - */ - if ((def->os.arch == VIR_ARCH_X86_64 || def->os.arch == VIR_ARCH_I686) && - compareAgainstHost) { - int hasSVM = cpuHasFeature(data, "svm"); - if (hasSVM < 0) - goto cleanup; - *hasHwVirt = hasSVM > 0 ? true : false; - } + virBuffer buf = VIR_BUFFER_INITIALIZER; - if ((cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) || - ((cpu->mode == VIR_CPU_MODE_HOST_MODEL) && - ARCH_IS_PPC64(def->os.arch))) { - const char *mode = virCPUModeTypeToString(cpu->mode); - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_HOST)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("CPU mode '%s' is not supported by QEMU" - " binary"), mode); - goto cleanup; - } - if (def->virtType != VIR_DOMAIN_VIRT_KVM) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("CPU mode '%s' is only supported with kvm"), - mode); - goto cleanup; - } - virBufferAddLit(buf, "host"); + if (!def || + (!def->manufacturer && !def->product && !def->version && + !def->serial && (!def->uuid || skip_uuid) && + def->sku && !def->family)) + return NULL; - if (def->os.arch == VIR_ARCH_ARMV7L && - host->arch == VIR_ARCH_AARCH64) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_AARCH64_OFF)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("QEMU binary does not support CPU " - "host-passthrough for armv7l on " - "aarch64 host")); - goto cleanup; - } + virBufferAddLit(&buf, "type=1"); - virBufferAddLit(buf, ",aarch64=off"); - } + /* 1:Manufacturer */ + if (def->manufacturer) + virBufferAsprintf(&buf, ",manufacturer=%s", + def->manufacturer); + /* 1:Product Name */ + if (def->product) + virBufferAsprintf(&buf, ",product=%s", def->product); + /* 1:Version */ + if (def->version) + virBufferAsprintf(&buf, ",version=%s", def->version); + /* 1:Serial Number */ + if (def->serial) + virBufferAsprintf(&buf, ",serial=%s", def->serial); + /* 1:UUID */ + if (def->uuid && !skip_uuid) + virBufferAsprintf(&buf, ",uuid=%s", def->uuid); + /* 1:SKU Number */ + if (def->sku) + virBufferAsprintf(&buf, ",sku=%s", def->sku); + /* 1:Family */ + if (def->family) + virBufferAsprintf(&buf, ",family=%s", def->family); - if (ARCH_IS_PPC64(def->os.arch) && - cpu->mode == VIR_CPU_MODE_HOST_MODEL && - def->cpu->model != NULL) { - virBufferAsprintf(buf, ",compat=%s", def->cpu->model); - } else { - featCpu = cpu; - } + if (virBufferCheckError(&buf) < 0) + goto error; - } else { - if (VIR_ALLOC(guest) < 0) - goto cleanup; - if (VIR_STRDUP(guest->vendor_id, cpu->vendor_id) < 0) - goto cleanup; + return virBufferContentAndReset(&buf); - if (compareAgainstHost) { - guest->arch = host->arch; - if (cpu->match == VIR_CPU_MATCH_MINIMUM) - preferred = host->model; - else - preferred = cpu->model; + error: + virBufferFreeAndReset(&buf); + return NULL; +} - guest->type = VIR_CPU_TYPE_GUEST; - guest->fallback = cpu->fallback; - if (cpuDecode(guest, data, - (const char **)cpus, ncpus, preferred) < 0) - goto cleanup; - } else { - guest->arch = def->os.arch; - if (VIR_STRDUP(guest->model, cpu->model) < 0) - goto cleanup; - } - virBufferAdd(buf, guest->model, -1); - if (guest->vendor_id) - virBufferAsprintf(buf, ",vendor=%s", guest->vendor_id); - featCpu = guest; - } +static char *qemuBuildSmbiosBaseBoardStr(virSysinfoBaseBoardDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; - if (featCpu) { - for (i = 0; i < featCpu->nfeatures; i++) { - char sign; - if (featCpu->features[i].policy == VIR_CPU_FEATURE_DISABLE) - sign = '-'; - else - sign = '+'; + if (!def) + return NULL; - virBufferAsprintf(buf, ",%c%s", sign, featCpu->features[i].name); - } - } + virBufferAddLit(&buf, "type=2"); - ret = 0; - cleanup: - virObjectUnref(caps); - VIR_FREE(compare_msg); - cpuDataFree(data); - cpuDataFree(hostData); - virCPUDefFree(guest); - virCPUDefFree(cpu); - return ret; + /* 2:Manufacturer */ + if (def->manufacturer) + virBufferAsprintf(&buf, ",manufacturer=%s", + def->manufacturer); + /* 2:Product Name */ + if (def->product) + virBufferAsprintf(&buf, ",product=%s", def->product); + /* 2:Version */ + if (def->version) + virBufferAsprintf(&buf, ",version=%s", def->version); + /* 2:Serial Number */ + if (def->serial) + virBufferAsprintf(&buf, ",serial=%s", def->serial); + /* 2:Asset Tag */ + if (def->asset) + virBufferAsprintf(&buf, ",asset=%s", def->asset); + /* 2:Location */ + if (def->location) + virBufferAsprintf(&buf, ",location=%s", def->location); + + if (virBufferCheckError(&buf) < 0) + goto error; + + return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; } -static int -qemuBuildCpuArgStr(virQEMUDriverPtr driver, - const virDomainDef *def, - virQEMUCapsPtr qemuCaps, - virArch hostarch, - char **opt, - bool *hasHwVirt, - bool migrating) +static char * +qemuBuildClockArgStr(virDomainClockDefPtr def) { - const char *default_model; - bool have_cpu = false; - int ret = -1; virBuffer buf = VIR_BUFFER_INITIALIZER; - size_t i; - *hasHwVirt = false; - - if (def->os.arch == VIR_ARCH_I686) - default_model = "qemu32"; - else - default_model = "qemu64"; + switch (def->offset) { + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + virBufferAddLit(&buf, "base=utc"); + break; - if (def->cpu && - (def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) { - if (qemuBuildCpuModelArgStr(driver, def, &buf, qemuCaps, - hasHwVirt, migrating) < 0) - goto cleanup; - have_cpu = true; - } else { - /* - * Need to force a 32-bit guest CPU type if - * - * 1. guest OS is i686 - * 2. host OS is x86_64 - * 3. emulator is qemu-kvm or kvm - * - * Or - * - * 1. guest OS is i686 - * 2. emulator is qemu-system-x86_64 - */ - if (def->os.arch == VIR_ARCH_I686 && - ((hostarch == VIR_ARCH_X86_64 && - strstr(def->emulator, "kvm")) || - strstr(def->emulator, "x86_64"))) { - virBufferAdd(&buf, default_model, -1); - have_cpu = true; - } - } + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: + virBufferAddLit(&buf, "base=localtime"); + break; - /* Handle paravirtual timers */ - for (i = 0; i < def->clock.ntimers; i++) { - virDomainTimerDefPtr timer = def->clock.timers[i]; + case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: { + time_t now = time(NULL); + struct tm nowbits; - if (timer->present == -1) - continue; + if (def->data.variable.basis == VIR_DOMAIN_CLOCK_BASIS_LOCALTIME) { + long localOffset; - if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK) { - virBufferAsprintf(&buf, "%s,%ckvmclock", - have_cpu ? "" : default_model, - timer->present ? '+' : '-'); - have_cpu = true; - } else if (timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK && - timer->present) { - virBufferAsprintf(&buf, "%s,hv_time", - have_cpu ? "" : default_model); - have_cpu = true; + /* in the case of basis='localtime', rather than trying to + * keep that basis (and associated offset from UTC) in the + * status and deal with adding in the difference each time + * there is an RTC_CHANGE event, it is simpler and less + * error prone to just convert the adjustment an offset + * from UTC right now (and change the status to + * "basis='utc' to reflect this). This eliminates + * potential errors in both RTC_CHANGE events and in + * migration (in the case that the status of DST, or the + * timezone of the destination host, changed relative to + * startup). + */ + if (virTimeLocalOffsetFromUTC(&localOffset) < 0) + goto error; + def->data.variable.adjustment += localOffset; + def->data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC; } - } - if (def->apic_eoi) { - char sign; - if (def->apic_eoi == VIR_TRISTATE_SWITCH_ON) - sign = '+'; - else - sign = '-'; + now += def->data.variable.adjustment; + gmtime_r(&now, &nowbits); - virBufferAsprintf(&buf, "%s,%ckvm_pv_eoi", - have_cpu ? "" : default_model, - sign); - have_cpu = true; - } + /* when an RTC_CHANGE event is received from qemu, we need to + * have the adjustment used at domain start time available to + * compute the new offset from UTC. As this new value is + * itself stored in def->data.variable.adjustment, we need to + * save a copy of it now. + */ + def->data.variable.adjustment0 = def->data.variable.adjustment; - if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK]) { - char sign; - if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK] == VIR_TRISTATE_SWITCH_ON) - sign = '+'; - else - sign = '-'; + virBufferAsprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d", + nowbits.tm_year + 1900, + nowbits.tm_mon + 1, + nowbits.tm_mday, + nowbits.tm_hour, + nowbits.tm_min, + nowbits.tm_sec); + } break; - virBufferAsprintf(&buf, "%s,%ckvm_pv_unhalt", - have_cpu ? "" : default_model, - sign); - have_cpu = true; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->offset)); + goto error; } - if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) { - if (!have_cpu) { - virBufferAdd(&buf, default_model, -1); - have_cpu = true; - } - - for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { - switch ((virDomainHyperv) i) { - case VIR_DOMAIN_HYPERV_RELAXED: - case VIR_DOMAIN_HYPERV_VAPIC: - if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON) - virBufferAsprintf(&buf, ",hv_%s", - virDomainHypervTypeToString(i)); + /* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */ + size_t i; + for (i = 0; i < def->ntimers; i++) { + if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) { + switch (def->timers[i]->track) { + case -1: /* unspecified - use hypervisor default */ break; - - case VIR_DOMAIN_HYPERV_SPINLOCKS: - if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON) - virBufferAsprintf(&buf, ",hv_spinlocks=0x%x", - def->hyperv_spinlocks); + case VIR_DOMAIN_TIMER_TRACK_BOOT: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc timer track '%s'"), + virDomainTimerTrackTypeToString(def->timers[i]->track)); + goto error; + case VIR_DOMAIN_TIMER_TRACK_GUEST: + virBufferAddLit(&buf, ",clock=vm"); break; - - /* coverity[dead_error_begin] */ - case VIR_DOMAIN_HYPERV_LAST: + case VIR_DOMAIN_TIMER_TRACK_WALL: + virBufferAddLit(&buf, ",clock=host"); break; } - } - } - - for (i = 0; i < def->npanics; i++) { - if (def->panics[i]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV) { - if (!have_cpu) { - virBufferAdd(&buf, default_model, -1); - have_cpu = true; - } - - virBufferAddLit(&buf, ",hv_crash"); - break; - } - } - - if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) { - if (!have_cpu) { - virBufferAdd(&buf, default_model, -1); - have_cpu = true; - } - for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) { - switch ((virDomainKVM) i) { - case VIR_DOMAIN_KVM_HIDDEN: - if (def->kvm_features[i] == VIR_TRISTATE_SWITCH_ON) - virBufferAddLit(&buf, ",kvm=off"); + switch (def->timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* This is the default - missed ticks delivered when + next scheduled, at normal rate */ break; - - /* coverity[dead_error_begin] */ - case VIR_DOMAIN_KVM_LAST: + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + /* deliver ticks at a faster rate until caught up */ + virBufferAddLit(&buf, ",driftfix=slew"); break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc timer tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy)); + goto error; } + break; /* no need to check other timers - there is only one rtc */ } } - if (def->features[VIR_DOMAIN_FEATURE_PMU]) { - virTristateSwitch pmu = def->features[VIR_DOMAIN_FEATURE_PMU]; - if (!have_cpu) - virBufferAdd(&buf, default_model, -1); - - virBufferAsprintf(&buf, ",pmu=%s", - virTristateSwitchTypeToString(pmu)); - have_cpu = true; - } - if (virBufferCheckError(&buf) < 0) - goto cleanup; - - *opt = virBufferContentAndReset(&buf); + goto error; - ret = 0; + return virBufferContentAndReset(&buf); - cleanup: - return ret; + error: + virBufferFreeAndReset(&buf); + return NULL; } static char * @@ -6723,11 +6756,9 @@ qemuBuildCommandLine(virConnectPtr conn, virErrorPtr originalError = NULL; size_t i, j; char uuid[VIR_UUID_STRING_BUFLEN]; - char *cpu; char *smp; int spicecnt = 0; int last_good_net = -1; - bool hasHwVirt = false; virCommandPtr cmd = NULL; bool allowReboot = true; bool emitBootindex = false; @@ -6758,7 +6789,6 @@ qemuBuildCommandLine(virConnectPtr conn, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, VIR_DOMAIN_CONTROLLER_TYPE_CCID, }; - virArch hostarch = virArchFromHost(); virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virBuffer boot_buf = VIR_BUFFER_INITIALIZER; char *boot_order_str = NULL, *boot_opts_str = NULL; @@ -6807,19 +6837,9 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuBuildMachineCommandLine(cmd, def, qemuCaps) < 0) goto error; - if (qemuBuildCpuArgStr(driver, def, qemuCaps, - hostarch, &cpu, &hasHwVirt, !!migrateURI) < 0) + if (qemuBuildCpuCommandLine(cmd, driver, def, qemuCaps, !!migrateURI) < 0) goto error; - if (cpu) { - virCommandAddArgList(cmd, "-cpu", cpu, NULL); - VIR_FREE(cpu); - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NESTING) && - hasHwVirt) - virCommandAddArg(cmd, "-enable-nesting"); - } - if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0) goto error; -- 2.5.0

Reorganize the module to put the function after the -cpu processing to form a logical order of processing for qemuBuildCommandLine working top down in the module. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 141 ++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 70 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9dbc4a3..70da921 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -790,6 +790,77 @@ qemuBuildCpuCommandLine(virCommandPtr cmd, } +/** Start Domain Loader (-bios, file=%s,if=pflash) arguments */ +static int +qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + int ret = -1; + virDomainLoaderDefPtr loader = def->os.loader; + virBuffer buf = VIR_BUFFER_INITIALIZER; + int unit = 0; + + if (!loader) + return 0; + + switch ((virDomainLoader) loader->type) { + case VIR_DOMAIN_LOADER_TYPE_ROM: + virCommandAddArg(cmd, "-bios"); + virCommandAddArg(cmd, loader->path); + break; + + case VIR_DOMAIN_LOADER_TYPE_PFLASH: + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_ACPI) && + def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("ACPI must be enabled in order to use UEFI")); + goto cleanup; + } + + virBufferAsprintf(&buf, + "file=%s,if=pflash,format=raw,unit=%d", + loader->path, unit); + unit++; + + if (loader->readonly) { + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this qemu doesn't support passing " + "readonly attribute")); + goto cleanup; + } + + virBufferAsprintf(&buf, ",readonly=%s", + virTristateSwitchTypeToString(loader->readonly)); + } + + virCommandAddArg(cmd, "-drive"); + virCommandAddArgBuffer(cmd, &buf); + + if (loader->nvram) { + virBufferFreeAndReset(&buf); + virBufferAsprintf(&buf, + "file=%s,if=pflash,format=raw,unit=%d", + loader->nvram, unit); + + virCommandAddArg(cmd, "-drive"); + virCommandAddArgBuffer(cmd, &buf); + } + break; + + case VIR_DOMAIN_LOADER_TYPE_LAST: + /* nada */ + break; + } + + ret = 0; + cleanup: + virBufferFreeAndReset(&buf); + return ret; +} + + static int qemuBuildObjectCommandLinePropsInternal(const char *key, const virJSONValue *value, @@ -6341,76 +6412,6 @@ qemuBuildChrDeviceCommandLine(virCommandPtr cmd, return 0; } -static int -qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, - virDomainDefPtr def, - virQEMUCapsPtr qemuCaps) -{ - int ret = -1; - virDomainLoaderDefPtr loader = def->os.loader; - virBuffer buf = VIR_BUFFER_INITIALIZER; - int unit = 0; - - if (!loader) - return 0; - - switch ((virDomainLoader) loader->type) { - case VIR_DOMAIN_LOADER_TYPE_ROM: - virCommandAddArg(cmd, "-bios"); - virCommandAddArg(cmd, loader->path); - break; - - case VIR_DOMAIN_LOADER_TYPE_PFLASH: - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_ACPI) && - def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("ACPI must be enabled in order to use UEFI")); - goto cleanup; - } - - virBufferAsprintf(&buf, - "file=%s,if=pflash,format=raw,unit=%d", - loader->path, unit); - unit++; - - if (loader->readonly) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("this qemu doesn't support passing " - "readonly attribute")); - goto cleanup; - } - - virBufferAsprintf(&buf, ",readonly=%s", - virTristateSwitchTypeToString(loader->readonly)); - } - - virCommandAddArg(cmd, "-drive"); - virCommandAddArgBuffer(cmd, &buf); - - if (loader->nvram) { - virBufferFreeAndReset(&buf); - virBufferAsprintf(&buf, - "file=%s,if=pflash,format=raw,unit=%d", - loader->nvram, unit); - - virCommandAddArg(cmd, "-drive"); - virCommandAddArgBuffer(cmd, &buf); - } - break; - - case VIR_DOMAIN_LOADER_TYPE_LAST: - /* nada */ - break; - } - - ret = 0; - cleanup: - virBufferFreeAndReset(&buf); - return ret; -} - - static char * qemuBuildTPMDevStr(const virDomainDef *def, virQEMUCapsPtr qemuCaps, -- 2.5.0

On Tue, Feb 16, 2016 at 19:44:16 -0500, John Ferlan wrote:
Reorganize the module to put the function after the -cpu processing to form a logical order of processing for qemuBuildCommandLine working top down in the module.
I'm not really a fan of this ...
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 141 ++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 70 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9dbc4a3..70da921 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -790,6 +790,77 @@ qemuBuildCpuCommandLine(virCommandPtr cmd, }
It doesn't remove a forward declaration of a static function, just shuffles code around.
+/** Start Domain Loader (-bios, file=%s,if=pflash) arguments */
This is yet another new style to the src/qemu subtree.
+static int +qemuBuildDomainLoaderCommandLine(virCommandPtr cmd,
I don't think it's worth moving the code so that it just is in the correct order. Is there any other reason? Peter

On 02/17/2016 03:44 AM, Peter Krempa wrote:
On Tue, Feb 16, 2016 at 19:44:16 -0500, John Ferlan wrote:
Reorganize the module to put the function after the -cpu processing to form a logical order of processing for qemuBuildCommandLine working top down in the module.
I'm not really a fan of this ...
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 141 ++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 70 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9dbc4a3..70da921 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -790,6 +790,77 @@ qemuBuildCpuCommandLine(virCommandPtr cmd, }
It doesn't remove a forward declaration of a static function, just shuffles code around.
+/** Start Domain Loader (-bios, file=%s,if=pflash) arguments */
This is yet another new style to the src/qemu subtree.
Markers mostly - easily removed
+static int +qemuBuildDomainLoaderCommandLine(virCommandPtr cmd,
I don't think it's worth moving the code so that it just is in the correct order. Is there any other reason?
Peter
For me it was a choice of haphazard and mostly random placement vs. ordered placement. It really doesn't matter that much to me; however, once I started it was just easier to move stuff. Although it really was horrible in difference management - especially when 300+ line functions were moved (and there's vary feelings on code that has 300+ line helper functions). That difference mgmt only became clear after I'd gone through a bunch of changes and started looking back at the pile. Of course a second deterrent becomes backport nightmares... The real goal was to change qemuBuildCommandLine to not have "much" inline code. It's currently >1100 lines long. Instead everything would be some sort of qemuBuildXXXCommandLine() type call. I think I can get that down to 250-300 lines. When adding new functions I was conflicted on placement, so that Type A thing kicked in and I started moving functions. I can change to purely create new helpers. It wasn't that difficult to move - although much more difficult to review. Along the way I've found some things that should be static (mostly all QEMU_CAPS_DEVICE related). It's really not all that difficult for me to change course and just go with the new qemuBuildXXXCommandLine functions. John

Reorganize the module to put all the -m argument processing code together after the domain loader to form a logical order of processing for qemuBuildCommandLine working top down in the module. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 217 ++++++++++++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 100 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 70da921..a8d1f4f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -861,6 +861,121 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, } +/** Start Memory (-m) arguments */ +static int +qemuBuildMemPathStr(virQEMUDriverConfigPtr cfg, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps, + virCommandPtr cmd) +{ + const long system_page_size = virGetSystemPageSizeKB(); + char *mem_path = NULL; + size_t i = 0; + + /* + * No-op if hugepages were not requested. + */ + if (!def->mem.nhugepages) + return 0; + + /* There is one special case: if user specified "huge" + * pages of regular system pages size. + * And there is nothing to do in this case. + */ + if (def->mem.hugepages[0].size == system_page_size) + return 0; + + if (!cfg->nhugetlbfs) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("hugetlbfs filesystem is not mounted " + "or disabled by administrator config")); + return -1; + } + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_PATH)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("hugepage backing not supported by '%s'"), + def->emulator); + return -1; + } + + if (!def->mem.hugepages[0].size) { + if (!(mem_path = qemuGetDefaultHugepath(cfg->hugetlbfs, + cfg->nhugetlbfs))) + return -1; + } else { + for (i = 0; i < cfg->nhugetlbfs; i++) { + if (cfg->hugetlbfs[i].size == def->mem.hugepages[0].size) + break; + } + + if (i == cfg->nhugetlbfs) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find any usable hugetlbfs " + "mount for %llu KiB"), + def->mem.hugepages[0].size); + return -1; + } + + if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[i]))) + return -1; + } + + virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", mem_path, NULL); + VIR_FREE(mem_path); + + return 0; +} + + +static int +qemuBuildMemCommandLine(virCommandPtr cmd, + virQEMUDriverConfigPtr cfg, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + if (qemuDomainDefValidateMemoryHotplug(def, qemuCaps, NULL) < 0) + goto error; + + virCommandAddArg(cmd, "-m"); + + if (virDomainDefHasMemoryHotplug(def)) { + /* Use the 'k' suffix to let qemu handle the units */ + virCommandAddArgFormat(cmd, "size=%lluk,slots=%u,maxmem=%lluk", + virDomainDefGetMemoryInitial(def), + def->mem.memory_slots, + def->mem.max_memory); + + } else { + virCommandAddArgFormat(cmd, "%llu", virDomainDefGetMemoryInitial(def) / 1024); + } + + /* + * Add '-mem-path' (and '-mem-prealloc') parameter here only if + * there is no numa node specified. + */ + if (!virDomainNumaGetNodeCount(def->numa) && + qemuBuildMemPathStr(cfg, def, qemuCaps, cmd) < 0) + goto error; + + if (def->mem.locked && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_MLOCK)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("memory locking not supported by QEMU binary")); + goto error; + } + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MLOCK)) { + virCommandAddArg(cmd, "-realtime"); + virCommandAddArgFormat(cmd, "mlock=%s", + def->mem.locked ? "on" : "off"); + } + + return 0; + + error: + return -1; +} + + static int qemuBuildObjectCommandLinePropsInternal(const char *key, const virJSONValue *value, @@ -5310,71 +5425,6 @@ qemuBuildSmpArgStr(const virDomainDef *def, } static int -qemuBuildMemPathStr(virQEMUDriverConfigPtr cfg, - virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virCommandPtr cmd) -{ - const long system_page_size = virGetSystemPageSizeKB(); - char *mem_path = NULL; - size_t i = 0; - - /* - * No-op if hugepages were not requested. - */ - if (!def->mem.nhugepages) - return 0; - - /* There is one special case: if user specified "huge" - * pages of regular system pages size. - * And there is nothing to do in this case. - */ - if (def->mem.hugepages[0].size == system_page_size) - return 0; - - if (!cfg->nhugetlbfs) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("hugetlbfs filesystem is not mounted " - "or disabled by administrator config")); - return -1; - } - - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_PATH)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("hugepage backing not supported by '%s'"), - def->emulator); - return -1; - } - - if (!def->mem.hugepages[0].size) { - if (!(mem_path = qemuGetDefaultHugepath(cfg->hugetlbfs, - cfg->nhugetlbfs))) - return -1; - } else { - for (i = 0; i < cfg->nhugetlbfs; i++) { - if (cfg->hugetlbfs[i].size == def->mem.hugepages[0].size) - break; - } - - if (i == cfg->nhugetlbfs) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find any usable hugetlbfs " - "mount for %llu KiB"), - def->mem.hugepages[0].size); - return -1; - } - - if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[i]))) - return -1; - } - - virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", mem_path, NULL); - VIR_FREE(mem_path); - - return 0; -} - -static int qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg, virDomainDefPtr def, virCommandPtr cmd, @@ -6844,44 +6894,11 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0) goto error; - if (!migrateURI && !snapshot && - qemuDomainAlignMemorySizes(def) < 0) - goto error; - - if (qemuDomainDefValidateMemoryHotplug(def, qemuCaps, NULL) < 0) - goto error; - - virCommandAddArg(cmd, "-m"); - - if (virDomainDefHasMemoryHotplug(def)) { - /* Use the 'k' suffix to let qemu handle the units */ - virCommandAddArgFormat(cmd, "size=%lluk,slots=%u,maxmem=%lluk", - virDomainDefGetMemoryInitial(def), - def->mem.memory_slots, - def->mem.max_memory); - - } else { - virCommandAddArgFormat(cmd, "%llu", virDomainDefGetMemoryInitial(def) / 1024); - } - - /* - * Add '-mem-path' (and '-mem-prealloc') parameter here only if - * there is no numa node specified. - */ - if (!virDomainNumaGetNodeCount(def->numa) && - qemuBuildMemPathStr(cfg, def, qemuCaps, cmd) < 0) + if (!migrateURI && !snapshot && qemuDomainAlignMemorySizes(def) < 0) goto error; - if (def->mem.locked && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_MLOCK)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("memory locking not supported by QEMU binary")); + if (qemuBuildMemCommandLine(cmd, cfg, def, qemuCaps) < 0) goto error; - } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MLOCK)) { - virCommandAddArg(cmd, "-realtime"); - virCommandAddArgFormat(cmd, "mlock=%s", - def->mem.locked ? "on" : "off"); - } virCommandAddArg(cmd, "-smp"); if (!(smp = qemuBuildSmpArgStr(def, qemuCaps))) -- 2.5.0

Reorganize the module to put all the -smp argument processing code together after the memory arguments to form a logical order of processing for qemuBuildCommandLine working top down in the module. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 102 ++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index a8d1f4f..c90650f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -976,6 +976,65 @@ qemuBuildMemCommandLine(virCommandPtr cmd, } +/** Start SMP (-smp) arguments */ +static char * +qemuBuildSmpArgStr(const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, "%u", virDomainDefGetVcpus(def)); + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SMP_TOPOLOGY)) { + if (virDomainDefHasVcpusOffline(def)) + virBufferAsprintf(&buf, ",maxcpus=%u", virDomainDefGetVcpusMax(def)); + /* sockets, cores, and threads are either all zero + * or all non-zero, thus checking one of them is enough */ + if (def->cpu && def->cpu->sockets) { + virBufferAsprintf(&buf, ",sockets=%u", def->cpu->sockets); + virBufferAsprintf(&buf, ",cores=%u", def->cpu->cores); + virBufferAsprintf(&buf, ",threads=%u", def->cpu->threads); + } else { + virBufferAsprintf(&buf, ",sockets=%u", virDomainDefGetVcpusMax(def)); + virBufferAsprintf(&buf, ",cores=%u", 1); + virBufferAsprintf(&buf, ",threads=%u", 1); + } + } else if (virDomainDefHasVcpusOffline(def)) { + virBufferFreeAndReset(&buf); + /* FIXME - consider hot-unplugging cpus after boot for older qemu */ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("setting current vcpu count less than maximum is " + "not supported with this QEMU binary")); + return NULL; + } + + if (virBufferCheckError(&buf) < 0) + return NULL; + + return virBufferContentAndReset(&buf); +} + + +static int +qemuBuildSmpCommandLine(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + char *smp; + + virCommandAddArg(cmd, "-smp"); + if (!(smp = qemuBuildSmpArgStr(def, qemuCaps))) + goto error; + virCommandAddArg(cmd, smp); + VIR_FREE(smp); + + return 0; + + error: + return -1; +} + + static int qemuBuildObjectCommandLinePropsInternal(const char *key, const virJSONValue *value, @@ -5387,43 +5446,6 @@ qemuBuildClockArgStr(virDomainClockDefPtr def) return NULL; } -static char * -qemuBuildSmpArgStr(const virDomainDef *def, - virQEMUCapsPtr qemuCaps) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, "%u", virDomainDefGetVcpus(def)); - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SMP_TOPOLOGY)) { - if (virDomainDefHasVcpusOffline(def)) - virBufferAsprintf(&buf, ",maxcpus=%u", virDomainDefGetVcpusMax(def)); - /* sockets, cores, and threads are either all zero - * or all non-zero, thus checking one of them is enough */ - if (def->cpu && def->cpu->sockets) { - virBufferAsprintf(&buf, ",sockets=%u", def->cpu->sockets); - virBufferAsprintf(&buf, ",cores=%u", def->cpu->cores); - virBufferAsprintf(&buf, ",threads=%u", def->cpu->threads); - } else { - virBufferAsprintf(&buf, ",sockets=%u", virDomainDefGetVcpusMax(def)); - virBufferAsprintf(&buf, ",cores=%u", 1); - virBufferAsprintf(&buf, ",threads=%u", 1); - } - } else if (virDomainDefHasVcpusOffline(def)) { - virBufferFreeAndReset(&buf); - /* FIXME - consider hot-unplugging cpus after boot for older qemu */ - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("setting current vcpu count less than maximum is " - "not supported with this QEMU binary")); - return NULL; - } - - if (virBufferCheckError(&buf) < 0) - return NULL; - - return virBufferContentAndReset(&buf); -} - static int qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg, virDomainDefPtr def, @@ -6807,7 +6829,6 @@ qemuBuildCommandLine(virConnectPtr conn, virErrorPtr originalError = NULL; size_t i, j; char uuid[VIR_UUID_STRING_BUFLEN]; - char *smp; int spicecnt = 0; int last_good_net = -1; virCommandPtr cmd = NULL; @@ -6900,11 +6921,8 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuBuildMemCommandLine(cmd, cfg, def, qemuCaps) < 0) goto error; - virCommandAddArg(cmd, "-smp"); - if (!(smp = qemuBuildSmpArgStr(def, qemuCaps))) + if (qemuBuildSmpCommandLine(cmd, def, qemuCaps) < 0) goto error; - virCommandAddArg(cmd, smp); - VIR_FREE(smp); if (def->niothreadids) { if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) { -- 2.5.0

On Tue, Feb 16, 2016 at 19:44:18 -0500, John Ferlan wrote:
Reorganize the module to put all the -smp argument processing code together after the memory arguments to form a logical order of processing for qemuBuildCommandLine working top down in the module.
Misleading commit message.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 102 ++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 42 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index a8d1f4f..c90650f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -976,6 +976,65 @@ qemuBuildMemCommandLine(virCommandPtr cmd, }
+/** Start SMP (-smp) arguments */
Wrong comment style.
+static char * +qemuBuildSmpArgStr(const virDomainDef *def, + virQEMUCapsPtr qemuCaps)
I don't think you need to move this at all.
+{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, "%u", virDomainDefGetVcpus(def)); + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SMP_TOPOLOGY)) { + if (virDomainDefHasVcpusOffline(def)) + virBufferAsprintf(&buf, ",maxcpus=%u", virDomainDefGetVcpusMax(def)); + /* sockets, cores, and threads are either all zero + * or all non-zero, thus checking one of them is enough */ + if (def->cpu && def->cpu->sockets) { + virBufferAsprintf(&buf, ",sockets=%u", def->cpu->sockets); + virBufferAsprintf(&buf, ",cores=%u", def->cpu->cores); + virBufferAsprintf(&buf, ",threads=%u", def->cpu->threads); + } else { + virBufferAsprintf(&buf, ",sockets=%u", virDomainDefGetVcpusMax(def)); + virBufferAsprintf(&buf, ",cores=%u", 1); + virBufferAsprintf(&buf, ",threads=%u", 1); + } + } else if (virDomainDefHasVcpusOffline(def)) { + virBufferFreeAndReset(&buf); + /* FIXME - consider hot-unplugging cpus after boot for older qemu */ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("setting current vcpu count less than maximum is " + "not supported with this QEMU binary")); + return NULL; + } + + if (virBufferCheckError(&buf) < 0) + return NULL; + + return virBufferContentAndReset(&buf); +} + + +static int +qemuBuildSmpCommandLine(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps)
+{ + char *smp; + + virCommandAddArg(cmd, "-smp"); + if (!(smp = qemuBuildSmpArgStr(def, qemuCaps)))
You could fold this code into qemuBuildSmpArgStr and just rename it rather than adding a new function.
+ goto error;
return -1; ...
+ virCommandAddArg(cmd, smp); + VIR_FREE(smp);
Peter

Reorganize the module to put all the IOThread argument processing code together after the -smp to form a logical order of processing for qemuBuildCommandLine working top down in the module. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 56 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c90650f..3000e8f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1035,6 +1035,42 @@ qemuBuildSmpCommandLine(virCommandPtr cmd, } +/** Start IOThread -object arguments */ +static int +qemuBuildIOThreadCommandLine(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + size_t i; + + if (def->niothreadids == 0) + return 0; + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("IOThreads not supported for this QEMU")); + goto error; + } + + /* Create iothread objects using the defined iothreadids list + * and the defined id and name from the list. These may be used + * by a disk definition which will associate to an iothread by + * supplying a value of an id from the list + */ + for (i = 0; i < def->niothreadids; i++) { + virCommandAddArg(cmd, "-object"); + virCommandAddArgFormat(cmd, "iothread,id=iothread%u", + def->iothreadids[i]->iothread_id); + } + + return 0; + + error: + return -1; + +} + + static int qemuBuildObjectCommandLinePropsInternal(const char *key, const virJSONValue *value, @@ -6924,24 +6960,8 @@ qemuBuildCommandLine(virConnectPtr conn, if (qemuBuildSmpCommandLine(cmd, def, qemuCaps) < 0) goto error; - if (def->niothreadids) { - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("IOThreads not supported for this QEMU")); - goto error; - } - - /* Create iothread objects using the defined iothreadids list - * and the defined id and name from the list. These may be used - * by a disk definition which will associate to an iothread by - * supplying a value of an id from the list - */ - for (i = 0; i < def->niothreadids; i++) { - virCommandAddArg(cmd, "-object"); - virCommandAddArgFormat(cmd, "iothread,id=iothread%u", - def->iothreadids[i]->iothread_id); - } - } + if (qemuBuildIOThreadCommandLine(cmd, def, qemuCaps) < 0) + goto error; if (virDomainNumaGetNodeCount(def->numa) && qemuBuildNumaArgStr(cfg, def, cmd, qemuCaps, nodeset) < 0) -- 2.5.0

On Tue, Feb 16, 2016 at 19:44:19 -0500, John Ferlan wrote:
Reorganize the module to put all the IOThread argument processing code together after the -smp to form a logical order of processing for qemuBuildCommandLine working top down in the module.
This is completely misleading including the title. This extracts the code from the command line builder.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 56 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c90650f..3000e8f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1035,6 +1035,42 @@ qemuBuildSmpCommandLine(virCommandPtr cmd, }
+/** Start IOThread -object arguments */
Wrong comment style.
+static int +qemuBuildIOThreadCommandLine(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + size_t i; + + if (def->niothreadids == 0) + return 0; + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("IOThreads not supported for this QEMU")); + goto error;
No need for the label.
+ } + + /* Create iothread objects using the defined iothreadids list + * and the defined id and name from the list. These may be used + * by a disk definition which will associate to an iothread by
ACK if you correctly state what this patch is doing in the commit message, fix the comment style and get rid of the error label. Peter

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@redhat.com> --- 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
participants (2)
-
John Ferlan
-
Peter Krempa