[libvirt] [PATCH 00/12] Split up qemu_command.c

Before disappearing too far down the rabbit hole... My recent foray into qemu_command.c to split out qemu_parse_command.c got me thinking that the module could use quite a bit of a reorganization. It's about 11,400 lines of all sorts of various functions. This started out as an attempt to order functions as they are used, but started turning into extracting certain API's/Helpers into other qemu_*.c modules. There's still quite a bit to do, but rather than hold onto it too long I figured I'd send what I have so far now (and keep working through while waiting for reviews). This is essentially all code motion with some function renames along the way. John Ferlan (12): qemu-command: Move qemuBuildTPMBackendStr qemu-command: Move qemuVirCommandGetFDSet qemu-command: Move qemuBuildTPMDevStr qemu-command: Move qemuVirCommandGetDevSet qemu: Move qemuPhysIfaceConnect to qemu_interface.c and rename qemu: Move qemuNetworkIfaceConnect to qemu_interface.c and rename qemu-command: Move qemuDomainSupports* functions qemu-command: Move qemuDomain*Address* functions to qemu_domain.c qemu-command: Move remaining qemuDomain* functions qemu-command: Move and rename qemuOpenVhostNet qemu-command: Move qemuNetworkPrepareDevices qemu-command: Move qemuAssign*Alias* API's into their own module po/POTFILES.in | 2 + src/Makefile.am | 1 + src/qemu/qemu_assign_alias.c | 468 +++++++ src/qemu/qemu_assign_alias.h | 60 + src/qemu/qemu_command.c | 3073 +++++------------------------------------- src/qemu/qemu_command.h | 48 - src/qemu/qemu_domain.c | 1646 ++++++++++++++++++++++ src/qemu/qemu_domain.h | 25 + src/qemu/qemu_driver.c | 1 + src/qemu/qemu_hotplug.c | 19 +- src/qemu/qemu_interface.c | 378 ++++++ src/qemu/qemu_interface.h | 22 + src/qemu/qemu_process.c | 56 +- tests/qemuhotplugtest.c | 1 + tests/qemuxml2argvtest.c | 1 + 15 files changed, 2978 insertions(+), 2823 deletions(-) create mode 100644 src/qemu/qemu_assign_alias.c create mode 100644 src/qemu/qemu_assign_alias.h -- 2.5.0

Move function closer to where it's called in qemuBuildTPMCommandLine Also adjust function header to fit current coding guidelines Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 180 ++++++++++++++++++++++++------------------------ 1 file changed, 91 insertions(+), 89 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 7a8ae73..495d57c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6858,95 +6858,6 @@ qemuBuildRNGDevStr(virDomainDefPtr def, } -static char *qemuBuildTPMBackendStr(const virDomainDef *def, - virCommandPtr cmd, - virQEMUCapsPtr qemuCaps, - const char *emulator, - int *tpmfd, int *cancelfd) -{ - const virDomainTPMDef *tpm = def->tpm; - virBuffer buf = VIR_BUFFER_INITIALIZER; - const char *type = virDomainTPMBackendTypeToString(tpm->type); - char *cancel_path = NULL, *devset = NULL; - const char *tpmdev; - - *tpmfd = -1; - *cancelfd = -1; - - virBufferAsprintf(&buf, "%s,id=tpm-%s", type, tpm->info.alias); - - switch (tpm->type) { - case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH)) - goto no_support; - - tpmdev = tpm->data.passthrough.source.data.file.path; - if (!(cancel_path = virTPMCreateCancelPath(tpmdev))) - goto error; - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ADD_FD)) { - *tpmfd = open(tpmdev, O_RDWR); - if (*tpmfd < 0) { - virReportSystemError(errno, _("Could not open TPM device %s"), - tpmdev); - goto error; - } - - virCommandPassFD(cmd, *tpmfd, - VIR_COMMAND_PASS_FD_CLOSE_PARENT); - devset = qemuVirCommandGetDevSet(cmd, *tpmfd); - if (devset == NULL) - goto error; - - *cancelfd = open(cancel_path, O_WRONLY); - if (*cancelfd < 0) { - virReportSystemError(errno, - _("Could not open TPM device's cancel " - "path %s"), cancel_path); - goto error; - } - VIR_FREE(cancel_path); - - virCommandPassFD(cmd, *cancelfd, - VIR_COMMAND_PASS_FD_CLOSE_PARENT); - cancel_path = qemuVirCommandGetDevSet(cmd, *cancelfd); - if (cancel_path == NULL) - goto error; - } - virBufferAddLit(&buf, ",path="); - virBufferEscape(&buf, ',', ",", "%s", devset ? devset : tpmdev); - - virBufferAddLit(&buf, ",cancel-path="); - virBufferEscape(&buf, ',', ",", "%s", cancel_path); - - VIR_FREE(devset); - VIR_FREE(cancel_path); - - break; - case VIR_DOMAIN_TPM_TYPE_LAST: - goto error; - } - - if (virBufferCheckError(&buf) < 0) - goto error; - - return virBufferContentAndReset(&buf); - - no_support: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("The QEMU executable %s does not support TPM " - "backend type %s"), - emulator, type); - - error: - VIR_FREE(devset); - VIR_FREE(cancel_path); - - virBufferFreeAndReset(&buf); - return NULL; -} - - static char *qemuBuildTPMDevStr(const virDomainDef *def, virQEMUCapsPtr qemuCaps, const char *emulator) @@ -9021,6 +8932,97 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, return ret; } + +static char * +qemuBuildTPMBackendStr(const virDomainDef *def, + virCommandPtr cmd, + virQEMUCapsPtr qemuCaps, + const char *emulator, + int *tpmfd, int *cancelfd) +{ + const virDomainTPMDef *tpm = def->tpm; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *type = virDomainTPMBackendTypeToString(tpm->type); + char *cancel_path = NULL, *devset = NULL; + const char *tpmdev; + + *tpmfd = -1; + *cancelfd = -1; + + virBufferAsprintf(&buf, "%s,id=tpm-%s", type, tpm->info.alias); + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH)) + goto no_support; + + tpmdev = tpm->data.passthrough.source.data.file.path; + if (!(cancel_path = virTPMCreateCancelPath(tpmdev))) + goto error; + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ADD_FD)) { + *tpmfd = open(tpmdev, O_RDWR); + if (*tpmfd < 0) { + virReportSystemError(errno, _("Could not open TPM device %s"), + tpmdev); + goto error; + } + + virCommandPassFD(cmd, *tpmfd, + VIR_COMMAND_PASS_FD_CLOSE_PARENT); + devset = qemuVirCommandGetDevSet(cmd, *tpmfd); + if (devset == NULL) + goto error; + + *cancelfd = open(cancel_path, O_WRONLY); + if (*cancelfd < 0) { + virReportSystemError(errno, + _("Could not open TPM device's cancel " + "path %s"), cancel_path); + goto error; + } + VIR_FREE(cancel_path); + + virCommandPassFD(cmd, *cancelfd, + VIR_COMMAND_PASS_FD_CLOSE_PARENT); + cancel_path = qemuVirCommandGetDevSet(cmd, *cancelfd); + if (cancel_path == NULL) + goto error; + } + virBufferAddLit(&buf, ",path="); + virBufferEscape(&buf, ',', ",", "%s", devset ? devset : tpmdev); + + virBufferAddLit(&buf, ",cancel-path="); + virBufferEscape(&buf, ',', ",", "%s", cancel_path); + + VIR_FREE(devset); + VIR_FREE(cancel_path); + + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + goto error; + } + + if (virBufferCheckError(&buf) < 0) + goto error; + + return virBufferContentAndReset(&buf); + + no_support: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("The QEMU executable %s does not support TPM " + "backend type %s"), + emulator, type); + + error: + VIR_FREE(devset); + VIR_FREE(cancel_path); + + virBufferFreeAndReset(&buf); + return NULL; +} + + static int qemuBuildTPMCommandLine(virDomainDefPtr def, virCommandPtr cmd, -- 2.5.0

The prefix contains a dash even though we use an underscore in the file name. Either use qemu_command, qemu or just no prefix at all (it's contained in the fucntion name anyway). On Mon, Feb 15, 2016 at 02:37:15PM -0500, John Ferlan wrote:
Move function closer to where it's called in qemuBuildTPMCommandLine
Also adjust function header to fit current coding guidelines
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 180 ++++++++++++++++++++++++------------------------ 1 file changed, 91 insertions(+), 89 deletions(-)
ACK, only code movement/whitespace changes.
@@ -9021,6 +8932,97 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, return ret; }
+ +static char * +qemuBuildTPMBackendStr(const virDomainDef *def, + virCommandPtr cmd, + virQEMUCapsPtr qemuCaps, + const char *emulator, + int *tpmfd, int *cancelfd)
You can also put these two parameters on separate lines. Jan

Move function closer to where it's used in qemuBuildTPMCommandLine Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 53 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 495d57c..db2ad05 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -153,32 +153,6 @@ VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST, "interleave"); /** - * qemuVirCommandGetFDSet: - * @cmd: the command to modify - * @fd: fd to reassign to the child - * - * Get the parameters for the QEMU -add-fd command line option - * for the given file descriptor. The file descriptor must previously - * have been 'transferred' in a virCommandPassFD() call. - * This function for example returns "set=10,fd=20". - */ -static char * -qemuVirCommandGetFDSet(virCommandPtr cmd, int fd) -{ - char *result = NULL; - int idx = virCommandPassFDGetFDIndex(cmd, fd); - - if (idx >= 0) { - ignore_value(virAsprintf(&result, "set=%d,fd=%d", idx, fd)); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("file descriptor %d has not been transferred"), fd); - } - - return result; -} - -/** * qemuVirCommandGetDevSet: * @cmd: the command to modify * @fd: fd to reassign to the child @@ -8933,6 +8907,33 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, } +/** + * qemuVirCommandGetFDSet: + * @cmd: the command to modify + * @fd: fd to reassign to the child + * + * Get the parameters for the QEMU -add-fd command line option + * for the given file descriptor. The file descriptor must previously + * have been 'transferred' in a virCommandPassFD() call. + * This function for example returns "set=10,fd=20". + */ +static char * +qemuVirCommandGetFDSet(virCommandPtr cmd, int fd) +{ + char *result = NULL; + int idx = virCommandPassFDGetFDIndex(cmd, fd); + + if (idx >= 0) { + ignore_value(virAsprintf(&result, "set=%d,fd=%d", idx, fd)); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("file descriptor %d has not been transferred"), fd); + } + + return result; +} + + static char * qemuBuildTPMBackendStr(const virDomainDef *def, virCommandPtr cmd, -- 2.5.0

On Mon, Feb 15, 2016 at 02:37:16PM -0500, John Ferlan wrote:
Move function closer to where it's used in qemuBuildTPMCommandLine
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 53 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-)
ACK, same comment about the prefix as in 1/12. Jan

Move function closer to where it's used in qemuBuildTPMCommandLine Also fix function header to match current coding practices Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 61 +++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index db2ad05..c8dda55 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6832,36 +6832,6 @@ qemuBuildRNGDevStr(virDomainDefPtr def, } -static char *qemuBuildTPMDevStr(const virDomainDef *def, - virQEMUCapsPtr qemuCaps, - const char *emulator) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - const virDomainTPMDef *tpm = def->tpm; - const char *model = virDomainTPMModelTypeToString(tpm->model); - - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_TIS)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("The QEMU executable %s does not support TPM " - "model %s"), - emulator, model); - goto error; - } - - virBufferAsprintf(&buf, "%s,tpmdev=tpm-%s,id=%s", - model, tpm->info.alias, tpm->info.alias); - - if (virBufferCheckError(&buf) < 0) - goto error; - - return virBufferContentAndReset(&buf); - - error: - virBufferFreeAndReset(&buf); - return NULL; -} - - static char *qemuBuildSmbiosBiosStr(virSysinfoBIOSDefPtr def) { virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -8907,6 +8877,37 @@ qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, } +static char * +qemuBuildTPMDevStr(const virDomainDef *def, + virQEMUCapsPtr qemuCaps, + const char *emulator) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const virDomainTPMDef *tpm = def->tpm; + const char *model = virDomainTPMModelTypeToString(tpm->model); + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_TIS)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("The QEMU executable %s does not support TPM " + "model %s"), + emulator, model); + goto error; + } + + virBufferAsprintf(&buf, "%s,tpmdev=tpm-%s,id=%s", + model, tpm->info.alias, tpm->info.alias); + + if (virBufferCheckError(&buf) < 0) + goto error; + + return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; +} + + /** * qemuVirCommandGetFDSet: * @cmd: the command to modify -- 2.5.0

On Mon, Feb 15, 2016 at 02:37:17PM -0500, John Ferlan wrote:
Move function closer to where it's used in qemuBuildTPMCommandLine
Also fix function header to match current coding practices
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 61 +++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 30 deletions(-)
ACK Jan

Move function closer to where it's used in qemuBuildTPMBackendStr Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c8dda55..04bfc75 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -153,32 +153,6 @@ VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST, "interleave"); /** - * qemuVirCommandGetDevSet: - * @cmd: the command to modify - * @fd: fd to reassign to the child - * - * Get the parameters for the QEMU path= parameter where a file - * descriptor is accessed via a file descriptor set, for example - * /dev/fdset/10. The file descriptor must previously have been - * 'transferred' in a virCommandPassFD() call. - */ -static char * -qemuVirCommandGetDevSet(virCommandPtr cmd, int fd) -{ - char *result = NULL; - int idx = virCommandPassFDGetFDIndex(cmd, fd); - - if (idx >= 0) { - ignore_value(virAsprintf(&result, "/dev/fdset/%d", idx)); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("file descriptor %d has not been transferred"), fd); - } - return result; -} - - -/** * qemuPhysIfaceConnect: * @def: the definition of the VM (needed by 802.1Qbh and audit) * @driver: pointer to the driver instance @@ -8935,6 +8909,32 @@ qemuVirCommandGetFDSet(virCommandPtr cmd, int fd) } +/** + * qemuVirCommandGetDevSet: + * @cmd: the command to modify + * @fd: fd to reassign to the child + * + * Get the parameters for the QEMU path= parameter where a file + * descriptor is accessed via a file descriptor set, for example + * /dev/fdset/10. The file descriptor must previously have been + * 'transferred' in a virCommandPassFD() call. + */ +static char * +qemuVirCommandGetDevSet(virCommandPtr cmd, int fd) +{ + char *result = NULL; + int idx = virCommandPassFDGetFDIndex(cmd, fd); + + if (idx >= 0) { + ignore_value(virAsprintf(&result, "/dev/fdset/%d", idx)); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("file descriptor %d has not been transferred"), fd); + } + return result; +} + + static char * qemuBuildTPMBackendStr(const virDomainDef *def, virCommandPtr cmd, -- 2.5.0

On Mon, Feb 15, 2016 at 02:37:18PM -0500, John Ferlan wrote:
Move function closer to where it's used in qemuBuildTPMBackendStr
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-)
ACK Jan

Move the misplaced function from qemu_command.c to qemu_interface.c since it's closer in functionality there and had less to do with building the command line. Rename function to qemuInterfacePhysicalConnect and modify callers. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 58 +++-------------------------------------------- src/qemu/qemu_command.h | 7 ------ src/qemu/qemu_hotplug.c | 5 ++-- src/qemu/qemu_interface.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_interface.h | 9 ++++++++ 5 files changed, 72 insertions(+), 64 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 04bfc75..be61b21 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -26,6 +26,7 @@ #include "qemu_command.h" #include "qemu_hostdev.h" #include "qemu_capabilities.h" +#include "qemu_interface.h" #include "cpu/cpu.h" #include "dirname.h" #include "passfd.h" @@ -153,60 +154,6 @@ VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST, "interleave"); /** - * qemuPhysIfaceConnect: - * @def: the definition of the VM (needed by 802.1Qbh and audit) - * @driver: pointer to the driver instance - * @net: pointer to the VM's interface description with direct device type - * @tapfd: array of file descriptor return value for the new device - * @tapfdSize: number of file descriptors in @tapfd - * @vmop: VM operation type - * - * Returns 0 on success or -1 in case of error. - */ -int -qemuPhysIfaceConnect(virDomainDefPtr def, - virQEMUDriverPtr driver, - virDomainNetDefPtr net, - int *tapfd, - size_t tapfdSize, - virNetDevVPortProfileOp vmop) -{ - int ret = -1; - char *res_ifname = NULL; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - unsigned int macvlan_create_flags = VIR_NETDEV_MACVLAN_CREATE_WITH_TAP; - - if (net->model && STREQ(net->model, "virtio")) - macvlan_create_flags |= VIR_NETDEV_MACVLAN_VNET_HDR; - - if (virNetDevMacVLanCreateWithVPortProfile(net->ifname, - &net->mac, - virDomainNetGetActualDirectDev(net), - virDomainNetGetActualDirectMode(net), - def->uuid, - virDomainNetGetActualVirtPortProfile(net), - &res_ifname, - vmop, cfg->stateDir, - tapfd, tapfdSize, - macvlan_create_flags) < 0) - goto cleanup; - - virDomainAuditNetDevice(def, net, res_ifname, true); - VIR_FREE(net->ifname); - net->ifname = res_ifname; - ret = 0; - - cleanup: - if (ret < 0) { - while (tapfdSize--) - VIR_FORCE_CLOSE(tapfd[tapfdSize]); - } - virObjectUnref(cfg); - return ret; -} - - -/** * qemuCreateInBridgePortWithHelper: * @cfg: the configuration object in which the helper name is looked up * @brname: the bridge name @@ -8490,7 +8437,8 @@ qemuBuildInterfaceCommandLine(virCommandPtr cmd, memset(tapfd, -1, tapfdSize * sizeof(tapfd[0])); - if (qemuPhysIfaceConnect(def, driver, net, tapfd, tapfdSize, vmop) < 0) + if (qemuInterfacePhysicalConnect(def, driver, net, + tapfd, tapfdSize, vmop) < 0) goto cleanup; } diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index f549aa5..2201c27 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -230,13 +230,6 @@ int qemuNetworkIfaceConnect(virDomainDefPtr def, size_t *tapfdSize) ATTRIBUTE_NONNULL(2); -int qemuPhysIfaceConnect(virDomainDefPtr def, - virQEMUDriverPtr driver, - virDomainNetDefPtr net, - int *tapfd, - size_t tapfdSize, - virNetDevVPortProfileOp vmop); - int qemuOpenVhostNet(virDomainDefPtr def, virDomainNetDefPtr net, virQEMUCapsPtr qemuCaps, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 8771780..bfc771d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -938,8 +938,9 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, if (VIR_ALLOC_N(vhostfd, vhostfdSize) < 0) goto cleanup; memset(vhostfd, -1, sizeof(*vhostfd) * vhostfdSize); - if (qemuPhysIfaceConnect(vm->def, driver, net, tapfd, tapfdSize, - VIR_NETDEV_VPORT_PROFILE_OP_CREATE) < 0) + if (qemuInterfacePhysicalConnect(vm->def, driver, net, + tapfd, tapfdSize, + VIR_NETDEV_VPORT_PROFILE_OP_CREATE) < 0) goto cleanup; iface_connected = true; if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, vhostfd, &vhostfdSize) < 0) diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index 4d55e4d..dbfdf0f 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -1,6 +1,7 @@ /* * qemu_interface.c: QEMU interface management * + * Copyright (C) 2015-2016 Red Hat, Inc. * Copyright IBM Corp. 2014 * * This library is free software; you can redistribute it and/or @@ -24,7 +25,9 @@ #include <config.h> #include "network_conf.h" +#include "domain_audit.h" #include "qemu_interface.h" +#include "viralloc.h" #include "virnetdev.h" #include "virnetdevtap.h" #include "virnetdevmacvlan.h" @@ -219,3 +222,57 @@ qemuInterfaceStopDevices(virDomainDefPtr def) } return 0; } + + +/** + * qemuInterfacePhysicalConnect: + * @def: the definition of the VM (needed by 802.1Qbh and audit) + * @driver: pointer to the driver instance + * @net: pointer to the VM's interface description with direct device type + * @tapfd: array of file descriptor return value for the new device + * @tapfdSize: number of file descriptors in @tapfd + * @vmop: VM operation type + * + * Returns 0 on success or -1 in case of error. + */ +int +qemuInterfacePhysicalConnect(virDomainDefPtr def, + virQEMUDriverPtr driver, + virDomainNetDefPtr net, + int *tapfd, + size_t tapfdSize, + virNetDevVPortProfileOp vmop) +{ + int ret = -1; + char *res_ifname = NULL; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + unsigned int macvlan_create_flags = VIR_NETDEV_MACVLAN_CREATE_WITH_TAP; + + if (net->model && STREQ(net->model, "virtio")) + macvlan_create_flags |= VIR_NETDEV_MACVLAN_VNET_HDR; + + if (virNetDevMacVLanCreateWithVPortProfile(net->ifname, + &net->mac, + virDomainNetGetActualDirectDev(net), + virDomainNetGetActualDirectMode(net), + def->uuid, + virDomainNetGetActualVirtPortProfile(net), + &res_ifname, + vmop, cfg->stateDir, + tapfd, tapfdSize, + macvlan_create_flags) < 0) + goto cleanup; + + virDomainAuditNetDevice(def, net, res_ifname, true); + VIR_FREE(net->ifname); + net->ifname = res_ifname; + ret = 0; + + cleanup: + if (ret < 0) { + while (tapfdSize--) + VIR_FORCE_CLOSE(tapfd[tapfdSize]); + } + virObjectUnref(cfg); + return ret; +} diff --git a/src/qemu/qemu_interface.h b/src/qemu/qemu_interface.h index b4c1efc..aa2791e 100644 --- a/src/qemu/qemu_interface.h +++ b/src/qemu/qemu_interface.h @@ -1,6 +1,7 @@ /* * qemu_interface.h: QEMU interface management * + * Copyright (C) 2014, 2016 Red Hat, Inc. * Copyright IBM Corp. 2014 * * This library is free software; you can redistribute it and/or @@ -25,10 +26,18 @@ # define __QEMU_INTERFACE_H__ # include "domain_conf.h" +# include "qemu_conf.h" int qemuInterfaceStartDevice(virDomainNetDefPtr net); int qemuInterfaceStartDevices(virDomainDefPtr def); int qemuInterfaceStopDevice(virDomainNetDefPtr net); int qemuInterfaceStopDevices(virDomainDefPtr def); +int qemuInterfacePhysicalConnect(virDomainDefPtr def, + virQEMUDriverPtr driver, + virDomainNetDefPtr net, + int *tapfd, + size_t tapfdSize, + virNetDevVPortProfileOp vmop); + #endif /* __QEMU_INTERFACE_H__ */ -- 2.5.0

On 02/15/2016 02:37 PM, John Ferlan wrote:
Move the misplaced function from qemu_command.c to qemu_interface.c since it's closer in functionality there and had less to do with building the command line.
Rename function to qemuInterfacePhysicalConnect and modify callers.
Signed-off-by: John Ferlan <jferlan@redhat.com> ---
Yay! Moving this (and qemuNetworkIfaceConnect()) to a separate file was the reason behind me requesting that qemu_interface.c be created for commit 82977058. I just never followed up and did it. I would suggest that this function be called qemuInterfaceDirectConnect() though - that matches with the name used for macvtap/macvlan connections in libvirt's xml config.
--- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -1,6 +1,7 @@ /* * qemu_interface.c: QEMU interface management * + * Copyright (C) 2015-2016 Red Hat, Inc.
If you're going to transfer copyright over from qemu_command.c to here, you should probably do it for the full range of the old file - 2006-2016.
* Copyright IBM Corp. 2014 * * This library is free software; you can redistribute it and/or
+} diff --git a/src/qemu/qemu_interface.h b/src/qemu/qemu_interface.h index b4c1efc..aa2791e 100644 --- a/src/qemu/qemu_interface.h +++ b/src/qemu/qemu_interface.h @@ -1,6 +1,7 @@ /* * qemu_interface.h: QEMU interface management * + * Copyright (C) 2014, 2016 Red Hat, Inc.
Same here. ACK with the copyright changes and rename of the function.

On 02/15/2016 05:31 PM, Laine Stump wrote:
On 02/15/2016 02:37 PM, John Ferlan wrote:
Move the misplaced function from qemu_command.c to qemu_interface.c since it's closer in functionality there and had less to do with building the command line.
Rename function to qemuInterfacePhysicalConnect and modify callers.
Signed-off-by: John Ferlan <jferlan@redhat.com> ---
Yay! Moving this (and qemuNetworkIfaceConnect()) to a separate file was the reason behind me requesting that qemu_interface.c be created for commit 82977058. I just never followed up and did it.
I would suggest that this function be called qemuInterfaceDirectConnect() though - that matches with the name used for macvtap/macvlan connections in libvirt's xml config.
Done.
--- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -1,6 +1,7 @@ /* * qemu_interface.c: QEMU interface management * + * Copyright (C) 2015-2016 Red Hat, Inc.
If you're going to transfer copyright over from qemu_command.c to here, you should probably do it for the full range of the old file - 2006-2016.
* Copyright IBM Corp. 2014 * * This library is free software; you can redistribute it and/or
+} diff --git a/src/qemu/qemu_interface.h b/src/qemu/qemu_interface.h index b4c1efc..aa2791e 100644 --- a/src/qemu/qemu_interface.h +++ b/src/qemu/qemu_interface.h @@ -1,6 +1,7 @@ /* * qemu_interface.h: QEMU interface management * + * Copyright (C) 2014, 2016 Red Hat, Inc.
Same here.
ACK with the copyright changes and rename of the function.
I was working under the assumption that the modules were created in 2014 with the .c changing in 2015, but the .h not changing there. I'll change it as suggested unless someone else has better advice... Thanks - for the quick look on the Interface functions! John

Move the misplaced function from qemu_command.c to qemu_interface.c since it's closer in functionality there and had less to do with building the command line. Rename function to qemuInterfaceNetworkConnect and modify callers. Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/qemu/qemu_command.c | 213 +------------------------------------------ src/qemu/qemu_command.h | 6 -- src/qemu/qemu_hotplug.c | 4 +- src/qemu/qemu_interface.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_interface.h | 6 ++ 6 files changed, 235 insertions(+), 219 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 4d82a8f..0ca9757 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -128,6 +128,7 @@ src/qemu/qemu_domain.c src/qemu/qemu_driver.c src/qemu/qemu_hostdev.c src/qemu/qemu_hotplug.c +src/qemu/qemu_interface.c src/qemu/qemu_migration.c src/qemu/qemu_monitor.c src/qemu/qemu_monitor_json.c diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index be61b21..4ed3349 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -29,7 +29,6 @@ #include "qemu_interface.h" #include "cpu/cpu.h" #include "dirname.h" -#include "passfd.h" #include "viralloc.h" #include "virlog.h" #include "virarch.h" @@ -153,214 +152,6 @@ VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST, "preferred", "interleave"); -/** - * qemuCreateInBridgePortWithHelper: - * @cfg: the configuration object in which the helper name is looked up - * @brname: the bridge name - * @ifname: the returned interface name - * @macaddr: the returned MAC address - * @tapfd: file descriptor return value for the new tap device - * @flags: OR of virNetDevTapCreateFlags: - - * VIR_NETDEV_TAP_CREATE_VNET_HDR - * - Enable IFF_VNET_HDR on the tap device - * - * This function creates a new tap device on a bridge using an external - * helper. The final name for the bridge will be stored in @ifname. - * - * Returns 0 in case of success or -1 on failure - */ -static int qemuCreateInBridgePortWithHelper(virQEMUDriverConfigPtr cfg, - const char *brname, - char **ifname, - int *tapfd, - unsigned int flags) -{ - virCommandPtr cmd; - char *errbuf = NULL, *cmdstr = NULL; - int pair[2] = { -1, -1 }; - - if ((flags & ~VIR_NETDEV_TAP_CREATE_VNET_HDR) != VIR_NETDEV_TAP_CREATE_IFUP) - return -1; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) { - virReportSystemError(errno, "%s", _("failed to create socket")); - return -1; - } - - if (!virFileIsExecutable(cfg->bridgeHelperName)) { - virReportSystemError(errno, _("'%s' is not a suitable bridge helper"), - cfg->bridgeHelperName); - return -1; - } - - cmd = virCommandNew(cfg->bridgeHelperName); - if (flags & VIR_NETDEV_TAP_CREATE_VNET_HDR) - virCommandAddArgFormat(cmd, "--use-vnet"); - virCommandAddArgFormat(cmd, "--br=%s", brname); - virCommandAddArgFormat(cmd, "--fd=%d", pair[1]); - virCommandSetErrorBuffer(cmd, &errbuf); - virCommandDoAsyncIO(cmd); - virCommandPassFD(cmd, pair[1], - VIR_COMMAND_PASS_FD_CLOSE_PARENT); - virCommandClearCaps(cmd); -#ifdef CAP_NET_ADMIN - virCommandAllowCap(cmd, CAP_NET_ADMIN); -#endif - if (virCommandRunAsync(cmd, NULL) < 0) { - *tapfd = -1; - goto cleanup; - } - - do { - *tapfd = recvfd(pair[0], 0); - } while (*tapfd < 0 && errno == EINTR); - - if (*tapfd < 0) { - char ebuf[1024]; - char *errstr = NULL; - - if (!(cmdstr = virCommandToString(cmd))) - goto cleanup; - virCommandAbort(cmd); - - if (errbuf && *errbuf && - virAsprintf(&errstr, "\nstderr=%s", errbuf) < 0) - goto cleanup; - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("%s: failed to communicate with bridge helper: %s%s"), - cmdstr, virStrerror(errno, ebuf, sizeof(ebuf)), - errstr ? errstr : ""); - VIR_FREE(errstr); - goto cleanup; - } - - if (virNetDevTapGetName(*tapfd, ifname) < 0 || - virCommandWait(cmd, NULL) < 0) { - VIR_FORCE_CLOSE(*tapfd); - *tapfd = -1; - } - - cleanup: - VIR_FREE(cmdstr); - VIR_FREE(errbuf); - virCommandFree(cmd); - VIR_FORCE_CLOSE(pair[0]); - return *tapfd < 0 ? -1 : 0; -} - -/* qemuNetworkIfaceConnect - *only* called if actualType is - * VIR_DOMAIN_NET_TYPE_NETWORK or VIR_DOMAIN_NET_TYPE_BRIDGE (i.e. if - * the connection is made with a tap device connecting to a bridge - * device) - */ -int -qemuNetworkIfaceConnect(virDomainDefPtr def, - virQEMUDriverPtr driver, - virDomainNetDefPtr net, - int *tapfd, - size_t *tapfdSize) -{ - const char *brname; - int ret = -1; - unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP; - bool template_ifname = false; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - const char *tunpath = "/dev/net/tun"; - - if (net->backend.tap) { - tunpath = net->backend.tap; - if (!(virQEMUDriverIsPrivileged(driver))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("cannot use custom tap device in session mode")); - goto cleanup; - } - } - - if (!(brname = virDomainNetGetActualBridgeName(net))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing bridge name")); - goto cleanup; - } - - if (!net->ifname || - STRPREFIX(net->ifname, VIR_NET_GENERATED_PREFIX) || - strchr(net->ifname, '%')) { - VIR_FREE(net->ifname); - if (VIR_STRDUP(net->ifname, VIR_NET_GENERATED_PREFIX "%d") < 0) - goto cleanup; - /* avoid exposing vnet%d in getXMLDesc or error outputs */ - template_ifname = true; - } - - if (net->model && STREQ(net->model, "virtio")) - tap_create_flags |= VIR_NETDEV_TAP_CREATE_VNET_HDR; - - if (virQEMUDriverIsPrivileged(driver)) { - if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac, - def->uuid, tunpath, tapfd, *tapfdSize, - virDomainNetGetActualVirtPortProfile(net), - virDomainNetGetActualVlan(net), - tap_create_flags) < 0) { - virDomainAuditNetDevice(def, net, tunpath, false); - goto cleanup; - } - if (virDomainNetGetActualBridgeMACTableManager(net) - == VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) { - /* libvirt is managing the FDB of the bridge this device - * is attaching to, so we need to turn off learning and - * unicast_flood on the device to prevent the kernel from - * adding any FDB entries for it. We will add add an fdb - * entry ourselves (during qemuInterfaceStartDevices(), - * using the MAC address from the interface config. - */ - if (virNetDevBridgePortSetLearning(brname, net->ifname, false) < 0) - goto cleanup; - if (virNetDevBridgePortSetUnicastFlood(brname, net->ifname, false) < 0) - goto cleanup; - } - } else { - if (qemuCreateInBridgePortWithHelper(cfg, brname, - &net->ifname, - tapfd, tap_create_flags) < 0) { - virDomainAuditNetDevice(def, net, tunpath, false); - goto cleanup; - } - /* qemuCreateInBridgePortWithHelper can only create a single FD */ - if (*tapfdSize > 1) { - VIR_WARN("Ignoring multiqueue network request"); - *tapfdSize = 1; - } - } - - virDomainAuditNetDevice(def, net, tunpath, true); - - if (cfg->macFilter && - ebtablesAddForwardAllowIn(driver->ebtables, - net->ifname, - &net->mac) < 0) - goto cleanup; - - if (net->filter && - virDomainConfNWFilterInstantiate(def->uuid, net) < 0) { - goto cleanup; - } - - ret = 0; - - cleanup: - if (ret < 0) { - size_t i; - for (i = 0; i < *tapfdSize && tapfd[i] >= 0; i++) - VIR_FORCE_CLOSE(tapfd[i]); - if (template_ifname) - VIR_FREE(net->ifname); - } - virObjectUnref(cfg); - - return ret; -} - static bool qemuDomainSupportsNicdev(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, @@ -8423,8 +8214,8 @@ qemuBuildInterfaceCommandLine(virCommandPtr cmd, memset(tapfd, -1, tapfdSize * sizeof(tapfd[0])); - if (qemuNetworkIfaceConnect(def, driver, net, - tapfd, &tapfdSize) < 0) + if (qemuInterfaceNetworkConnect(def, driver, net, + tapfd, &tapfdSize) < 0) goto cleanup; } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { tapfdSize = net->driver.virtio.queues; diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 2201c27..5fb91e5 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -223,12 +223,6 @@ char *qemuBuildHubDevStr(virDomainDefPtr def, char *qemuBuildRedirdevDevStr(virDomainDefPtr def, virDomainRedirdevDefPtr dev, virQEMUCapsPtr qemuCaps); -int qemuNetworkIfaceConnect(virDomainDefPtr def, - virQEMUDriverPtr driver, - virDomainNetDefPtr net, - int *tapfd, - size_t *tapfdSize) - ATTRIBUTE_NONNULL(2); int qemuOpenVhostNet(virDomainDefPtr def, virDomainNetDefPtr net, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index bfc771d..e635fab 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -922,8 +922,8 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, if (VIR_ALLOC_N(vhostfd, vhostfdSize) < 0) goto cleanup; memset(vhostfd, -1, sizeof(*vhostfd) * vhostfdSize); - if (qemuNetworkIfaceConnect(vm->def, driver, net, - tapfd, &tapfdSize) < 0) + if (qemuInterfaceNetworkConnect(vm->def, driver, net, + tapfd, &tapfdSize) < 0) goto cleanup; iface_connected = true; if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, vhostfd, &vhostfdSize) < 0) diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index dbfdf0f..2584236 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -26,14 +26,22 @@ #include "network_conf.h" #include "domain_audit.h" +#include "domain_nwfilter.h" #include "qemu_interface.h" +#include "passfd.h" #include "viralloc.h" +#include "virlog.h" +#include "virstring.h" #include "virnetdev.h" #include "virnetdevtap.h" #include "virnetdevmacvlan.h" #include "virnetdevbridge.h" #include "virnetdevvportprofile.h" +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.qemu_interface"); + /** * qemuInterfaceStartDevice: * @net: net device to start @@ -276,3 +284,219 @@ qemuInterfacePhysicalConnect(virDomainDefPtr def, virObjectUnref(cfg); return ret; } + + +/** + * qemuCreateInBridgePortWithHelper: + * @cfg: the configuration object in which the helper name is looked up + * @brname: the bridge name + * @ifname: the returned interface name + * @macaddr: the returned MAC address + * @tapfd: file descriptor return value for the new tap device + * @flags: OR of virNetDevTapCreateFlags: + + * VIR_NETDEV_TAP_CREATE_VNET_HDR + * - Enable IFF_VNET_HDR on the tap device + * + * This function creates a new tap device on a bridge using an external + * helper. The final name for the bridge will be stored in @ifname. + * + * Returns 0 in case of success or -1 on failure + */ +static int +qemuCreateInBridgePortWithHelper(virQEMUDriverConfigPtr cfg, + const char *brname, + char **ifname, + int *tapfd, + unsigned int flags) +{ + virCommandPtr cmd; + char *errbuf = NULL, *cmdstr = NULL; + int pair[2] = { -1, -1 }; + + if ((flags & ~VIR_NETDEV_TAP_CREATE_VNET_HDR) != VIR_NETDEV_TAP_CREATE_IFUP) + return -1; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) { + virReportSystemError(errno, "%s", _("failed to create socket")); + return -1; + } + + if (!virFileIsExecutable(cfg->bridgeHelperName)) { + virReportSystemError(errno, _("'%s' is not a suitable bridge helper"), + cfg->bridgeHelperName); + return -1; + } + + cmd = virCommandNew(cfg->bridgeHelperName); + if (flags & VIR_NETDEV_TAP_CREATE_VNET_HDR) + virCommandAddArgFormat(cmd, "--use-vnet"); + virCommandAddArgFormat(cmd, "--br=%s", brname); + virCommandAddArgFormat(cmd, "--fd=%d", pair[1]); + virCommandSetErrorBuffer(cmd, &errbuf); + virCommandDoAsyncIO(cmd); + virCommandPassFD(cmd, pair[1], + VIR_COMMAND_PASS_FD_CLOSE_PARENT); + virCommandClearCaps(cmd); +#ifdef CAP_NET_ADMIN + virCommandAllowCap(cmd, CAP_NET_ADMIN); +#endif + if (virCommandRunAsync(cmd, NULL) < 0) { + *tapfd = -1; + goto cleanup; + } + + do { + *tapfd = recvfd(pair[0], 0); + } while (*tapfd < 0 && errno == EINTR); + + if (*tapfd < 0) { + char ebuf[1024]; + char *errstr = NULL; + + if (!(cmdstr = virCommandToString(cmd))) + goto cleanup; + virCommandAbort(cmd); + + if (errbuf && *errbuf && + virAsprintf(&errstr, "\nstderr=%s", errbuf) < 0) + goto cleanup; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s: failed to communicate with bridge helper: %s%s"), + cmdstr, virStrerror(errno, ebuf, sizeof(ebuf)), + errstr ? errstr : ""); + VIR_FREE(errstr); + goto cleanup; + } + + if (virNetDevTapGetName(*tapfd, ifname) < 0 || + virCommandWait(cmd, NULL) < 0) { + VIR_FORCE_CLOSE(*tapfd); + *tapfd = -1; + } + + cleanup: + VIR_FREE(cmdstr); + VIR_FREE(errbuf); + virCommandFree(cmd); + VIR_FORCE_CLOSE(pair[0]); + return *tapfd < 0 ? -1 : 0; +} + +/* qemuInterfaceNetworkConnect: + * @def: the definition of the VM + * @driver: qemu driver data + * @net: pointer to the VM's interface description + * @tapfd: array of file descriptor return value for the new device + * @tapfdsize: number of file descriptors in @tapfd + * + * Called *only* called if actualType is VIR_DOMAIN_NET_TYPE_NETWORK or + * VIR_DOMAIN_NET_TYPE_BRIDGE (i.e. if the connection is made with a tap + * device connecting to a bridge device) + */ +int +qemuInterfaceNetworkConnect(virDomainDefPtr def, + virQEMUDriverPtr driver, + virDomainNetDefPtr net, + int *tapfd, + size_t *tapfdSize) +{ + const char *brname; + int ret = -1; + unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP; + bool template_ifname = false; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + const char *tunpath = "/dev/net/tun"; + + if (net->backend.tap) { + tunpath = net->backend.tap; + if (!(virQEMUDriverIsPrivileged(driver))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot use custom tap device in session mode")); + goto cleanup; + } + } + + if (!(brname = virDomainNetGetActualBridgeName(net))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing bridge name")); + goto cleanup; + } + + if (!net->ifname || + STRPREFIX(net->ifname, VIR_NET_GENERATED_PREFIX) || + strchr(net->ifname, '%')) { + VIR_FREE(net->ifname); + if (VIR_STRDUP(net->ifname, VIR_NET_GENERATED_PREFIX "%d") < 0) + goto cleanup; + /* avoid exposing vnet%d in getXMLDesc or error outputs */ + template_ifname = true; + } + + if (net->model && STREQ(net->model, "virtio")) + tap_create_flags |= VIR_NETDEV_TAP_CREATE_VNET_HDR; + + if (virQEMUDriverIsPrivileged(driver)) { + if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac, + def->uuid, tunpath, tapfd, *tapfdSize, + virDomainNetGetActualVirtPortProfile(net), + virDomainNetGetActualVlan(net), + tap_create_flags) < 0) { + virDomainAuditNetDevice(def, net, tunpath, false); + goto cleanup; + } + if (virDomainNetGetActualBridgeMACTableManager(net) + == VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) { + /* libvirt is managing the FDB of the bridge this device + * is attaching to, so we need to turn off learning and + * unicast_flood on the device to prevent the kernel from + * adding any FDB entries for it. We will add add an fdb + * entry ourselves (during qemuInterfaceStartDevices(), + * using the MAC address from the interface config. + */ + if (virNetDevBridgePortSetLearning(brname, net->ifname, false) < 0) + goto cleanup; + if (virNetDevBridgePortSetUnicastFlood(brname, net->ifname, false) < 0) + goto cleanup; + } + } else { + if (qemuCreateInBridgePortWithHelper(cfg, brname, + &net->ifname, + tapfd, tap_create_flags) < 0) { + virDomainAuditNetDevice(def, net, tunpath, false); + goto cleanup; + } + /* qemuCreateInBridgePortWithHelper can only create a single FD */ + if (*tapfdSize > 1) { + VIR_WARN("Ignoring multiqueue network request"); + *tapfdSize = 1; + } + } + + virDomainAuditNetDevice(def, net, tunpath, true); + + if (cfg->macFilter && + ebtablesAddForwardAllowIn(driver->ebtables, + net->ifname, + &net->mac) < 0) + goto cleanup; + + if (net->filter && + virDomainConfNWFilterInstantiate(def->uuid, net) < 0) { + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret < 0) { + size_t i; + for (i = 0; i < *tapfdSize && tapfd[i] >= 0; i++) + VIR_FORCE_CLOSE(tapfd[i]); + if (template_ifname) + VIR_FREE(net->ifname); + } + virObjectUnref(cfg); + + return ret; +} diff --git a/src/qemu/qemu_interface.h b/src/qemu/qemu_interface.h index aa2791e..9cc7331 100644 --- a/src/qemu/qemu_interface.h +++ b/src/qemu/qemu_interface.h @@ -40,4 +40,10 @@ int qemuInterfacePhysicalConnect(virDomainDefPtr def, size_t tapfdSize, virNetDevVPortProfileOp vmop); +int qemuInterfaceNetworkConnect(virDomainDefPtr def, + virQEMUDriverPtr driver, + virDomainNetDefPtr net, + int *tapfd, + size_t *tapfdSize) + ATTRIBUTE_NONNULL(2); #endif /* __QEMU_INTERFACE_H__ */ -- 2.5.0

On 02/15/2016 02:37 PM, John Ferlan wrote:
Move the misplaced function from qemu_command.c to qemu_interface.c since it's closer in functionality there and had less to do with building the command line.
Rename function to qemuInterfaceNetworkConnect and modify callers.
This function really is connecting to a bridge (either one that is part of a libvirt network, or a host bridge defined outside libvirt), so I would suggest qemuInterfaceBridgeConnect(). (I had considered suggesting qemuInterfaceTapConnect (and the other one qemuInterfaceMacvlanConnect()), but I think I like naming them *Bridge* and *Direct* instead). ACK with the name change. (Of course in the end both functions should end up being called by some function called qemuInterfaceConnect(), and that function will be the only one called from qemu_command.c. No sense in going wild right out of the chute though, eh? :-)

On 02/15/2016 05:38 PM, Laine Stump wrote:
On 02/15/2016 02:37 PM, John Ferlan wrote:
Move the misplaced function from qemu_command.c to qemu_interface.c since it's closer in functionality there and had less to do with building the command line.
Rename function to qemuInterfaceNetworkConnect and modify callers.
This function really is connecting to a bridge (either one that is part of a libvirt network, or a host bridge defined outside libvirt), so I would suggest qemuInterfaceBridgeConnect(). (I had considered suggesting qemuInterfaceTapConnect (and the other one qemuInterfaceMacvlanConnect()), but I think I like naming them *Bridge* and *Direct* instead).
ACK with the name change.
OK - done
(Of course in the end both functions should end up being called by some function called qemuInterfaceConnect(), and that function will be the only one called from qemu_command.c. No sense in going wild right out of the chute though, eh? :-)
Baby steps... It's the new normal. Although I'll keep this in mind... Could be a challenge due to qemu_hotplug usage and calling sequence, but we'll see. John

Move qemuDomainSupportsNicdev and qemuDomainSupportsNetdev into qemu_domain.c and expose from there. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 29 ----------------------------- src/qemu/qemu_domain.c | 29 +++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 9 +++++++++ 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 4ed3349..d02d7cb 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -152,35 +152,6 @@ VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST, "preferred", "interleave"); -static bool -qemuDomainSupportsNicdev(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainNetDefPtr net) -{ - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) - return false; - - /* non-virtio ARM nics require legacy -net nic */ - if (((def->os.arch == VIR_ARCH_ARMV7L) || - (def->os.arch == VIR_ARCH_AARCH64)) && - net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO && - net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) - return false; - - return true; -} - -static bool -qemuDomainSupportsNetdev(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainNetDefPtr net) -{ - if (!qemuDomainSupportsNicdev(def, qemuCaps, net)) - return false; - return virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV); -} - - static int qemuBuildObjectCommandLinePropsInternal(const char *key, const virJSONValue *value, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 495c76b..686c9e4 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -4366,3 +4366,32 @@ qemuDomainDetectVcpuPids(virQEMUDriverPtr driver, priv->vcpupids = cpupids; return ncpupids; } + + +bool +qemuDomainSupportsNicdev(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainNetDefPtr net) +{ + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) + return false; + + /* non-virtio ARM nics require legacy -net nic */ + if (((def->os.arch == VIR_ARCH_ARMV7L) || + (def->os.arch == VIR_ARCH_AARCH64)) && + net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO && + net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + return false; + + return true; +} + +bool +qemuDomainSupportsNetdev(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainNetDefPtr net) +{ + if (!qemuDomainSupportsNicdev(def, qemuCaps, net)) + return false; + return virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV); +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 285af8c..95b2e9d 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -513,4 +513,13 @@ pid_t qemuDomainGetVcpuPid(virDomainObjPtr vm, unsigned int vcpu); int qemuDomainDetectVcpuPids(virQEMUDriverPtr driver, virDomainObjPtr vm, int asyncJob); +bool qemuDomainSupportsNicdev(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainNetDefPtr net); + +bool qemuDomainSupportsNetdev(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainNetDefPtr net); + + #endif /* __QEMU_DOMAIN_H__ */ -- 2.5.0

Here I'd rather drop the command from the prefix completely, since the fuctions get moved. On Mon, Feb 15, 2016 at 02:37:21PM -0500, John Ferlan wrote:
Move qemuDomainSupportsNicdev and qemuDomainSupportsNetdev into qemu_domain.c and expose from there.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 29 ----------------------------- src/qemu/qemu_domain.c | 29 +++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 9 +++++++++ 3 files changed, 38 insertions(+), 29 deletions(-)
ACK Jan

Move the functions into qemu_domain.c - additionally move any supporting static functions. Make qemuDomainSupportsPCI non static. Also, move and rename the following: qemuSetSCSIControllerModel to qemuDomainSetSCSIControllerModel qemuCollectPCIAddress to qemuDomainCollectPCIAddress qemuValidateDevicePCISlotsPIIX3 to qemuDomainValidateDevicePCISlotsPIIX3 qemuAssignDevicePCISlots to qemuDomainAssignDevicePCISlots Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 1597 +---------------------------------------------- src/qemu/qemu_command.h | 12 - src/qemu/qemu_domain.c | 1592 ++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 12 + 4 files changed, 1610 insertions(+), 1603 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d02d7cb..f4b9dcd 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -66,11 +66,6 @@ VIR_LOG_INIT("qemu.qemu_command"); -#define VIO_ADDR_NET 0x1000ul -#define VIO_ADDR_SCSI 0x2000ul -#define VIO_ADDR_SERIAL 0x30000000ul -#define VIO_ADDR_NVRAM 0x3000ul - VIR_ENUM_DECL(virDomainDiskQEMUBus) VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST, "ide", @@ -500,64 +495,6 @@ static int qemuAssignDeviceDiskAliasFixed(virDomainDiskDefPtr disk) return 0; } -static int -qemuSetSCSIControllerModel(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - int *model) -{ - if (*model > 0) { - switch (*model) { - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC: - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU doesn't support " - "the LSI 53C895A SCSI controller")); - return -1; - } - break; - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI: - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU doesn't support " - "virtio scsi controller")); - return -1; - } - break; - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI: - /*TODO: need checking work here if necessary */ - break; - case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078: - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_MEGASAS)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU doesn't support " - "the LSI SAS1078 controller")); - return -1; - } - break; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported controller model: %s"), - virDomainControllerModelSCSITypeToString(*model)); - return -1; - } - } else { - if (ARCH_IS_PPC64(def->os.arch) && - STRPREFIX(def->os.machine, "pseries")) { - *model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI; - } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI)) { - *model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC; - } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI)) { - *model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to determine model for scsi controller")); - return -1; - } - } - - return 0; -} - /* Our custom -drive naming scheme used with id= */ static int qemuAssignDeviceDiskAliasCustom(virDomainDefPtr def, @@ -573,7 +510,8 @@ qemuAssignDeviceDiskAliasCustom(virDomainDefPtr def, virDomainDeviceFindControllerModel(def, &disk->info, VIR_DOMAIN_CONTROLLER_TYPE_SCSI); - if ((qemuSetSCSIControllerModel(def, qemuCaps, &controllerModel)) < 0) + if ((qemuDomainSetSCSIControllerModel(def, qemuCaps, + &controllerModel)) < 0) return -1; } @@ -926,1530 +864,6 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) } -static void -qemuDomainPrimeVirtioDeviceAddresses(virDomainDefPtr def, - virDomainDeviceAddressType type) -{ - /* - declare address-less virtio devices to be of address type 'type' - disks, networks, consoles, controllers, memballoon and rng in this - order - if type is ccw filesystem devices are declared to be of address type ccw - */ - size_t i; - - for (i = 0; i < def->ndisks; i++) { - if (def->disks[i]->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && - def->disks[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - def->disks[i]->info.type = type; - } - - for (i = 0; i < def->nnets; i++) { - if (STREQ(def->nets[i]->model, "virtio") && - def->nets[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - def->nets[i]->info.type = type; - } - } - - for (i = 0; i < def->ninputs; i++) { - if (def->inputs[i]->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && - def->inputs[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - def->inputs[i]->info.type = type; - } - - for (i = 0; i < def->ncontrollers; i++) { - if ((def->controllers[i]->type == - VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL || - def->controllers[i]->type == - VIR_DOMAIN_CONTROLLER_TYPE_SCSI) && - def->controllers[i]->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - def->controllers[i]->info.type = type; - } - - if (def->memballoon && - def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO && - def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - def->memballoon->info.type = type; - - for (i = 0; i < def->nrngs; i++) { - if (def->rngs[i]->model == VIR_DOMAIN_RNG_MODEL_VIRTIO && - def->rngs[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - def->rngs[i]->info.type = type; - } - - if (type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { - for (i = 0; i < def->nfss; i++) { - if (def->fss[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - def->fss[i]->info.type = type; - } - } -} - - -/* - * Three steps populating CCW devnos - * 1. Allocate empty address set - * 2. Gather addresses with explicit devno - * 3. Assign defaults to the rest - */ -static int -qemuDomainAssignS390Addresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainObjPtr obj) -{ - int ret = -1; - virDomainCCWAddressSetPtr addrs = NULL; - qemuDomainObjPrivatePtr priv = NULL; - - if (qemuDomainMachineIsS390CCW(def) && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) { - qemuDomainPrimeVirtioDeviceAddresses( - def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW); - - if (!(addrs = virDomainCCWAddressSetCreate())) - goto cleanup; - - if (virDomainDeviceInfoIterate(def, virDomainCCWAddressValidate, - addrs) < 0) - goto cleanup; - - if (virDomainDeviceInfoIterate(def, virDomainCCWAddressAllocate, - addrs) < 0) - goto cleanup; - } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) { - /* deal with legacy virtio-s390 */ - qemuDomainPrimeVirtioDeviceAddresses( - def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390); - } - - if (obj && obj->privateData) { - priv = obj->privateData; - if (addrs) { - /* if this is the live domain object, we persist the CCW addresses*/ - virDomainCCWAddressSetFree(priv->ccwaddrs); - priv->persistentAddrs = 1; - priv->ccwaddrs = addrs; - addrs = NULL; - } else { - priv->persistentAddrs = 0; - } - } - ret = 0; - - cleanup: - virDomainCCWAddressSetFree(addrs); - - return ret; -} - -static int -qemuDomainAssignARMVirtioMMIOAddresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps) -{ - if (((def->os.arch == VIR_ARCH_ARMV7L) || - (def->os.arch == VIR_ARCH_AARCH64)) && - (STRPREFIX(def->os.machine, "vexpress-") || - STREQ(def->os.machine, "virt") || - STRPREFIX(def->os.machine, "virt-")) && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_MMIO)) { - qemuDomainPrimeVirtioDeviceAddresses( - def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO); - } - return 0; -} - -static int -qemuSpaprVIOFindByReg(virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, - virDomainDeviceInfoPtr info, void *opaque) -{ - virDomainDeviceInfoPtr target = opaque; - - if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) - return 0; - - /* Match a dev that has a reg, is not us, and has a matching reg */ - if (info->addr.spaprvio.has_reg && info != target && - info->addr.spaprvio.reg == target->addr.spaprvio.reg) - /* Has to be < 0 so virDomainDeviceInfoIterate() will exit */ - return -1; - - return 0; -} - -static int -qemuAssignSpaprVIOAddress(virDomainDefPtr def, virDomainDeviceInfoPtr info, - unsigned long long default_reg) -{ - bool user_reg; - int ret; - - if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) - return 0; - - /* Check if the user has assigned the reg already, if so use it */ - user_reg = info->addr.spaprvio.has_reg; - if (!user_reg) { - info->addr.spaprvio.reg = default_reg; - info->addr.spaprvio.has_reg = true; - } - - ret = virDomainDeviceInfoIterate(def, qemuSpaprVIOFindByReg, info); - while (ret != 0) { - if (user_reg) { - virReportError(VIR_ERR_XML_ERROR, - _("spapr-vio address %#llx already in use"), - info->addr.spaprvio.reg); - return -EEXIST; - } - - /* We assigned the reg, so try a new value */ - info->addr.spaprvio.reg += 0x1000; - ret = virDomainDeviceInfoIterate(def, qemuSpaprVIOFindByReg, info); - } - - return 0; -} - - -static int -qemuDomainAssignVirtioSerialAddresses(virDomainDefPtr def, - virDomainObjPtr obj) -{ - int ret = -1; - size_t i; - virDomainVirtioSerialAddrSetPtr addrs = NULL; - qemuDomainObjPrivatePtr priv = NULL; - - if (!(addrs = virDomainVirtioSerialAddrSetCreate())) - goto cleanup; - - if (virDomainVirtioSerialAddrSetAddControllers(addrs, def) < 0) - goto cleanup; - - if (virDomainDeviceInfoIterate(def, virDomainVirtioSerialAddrReserve, - addrs) < 0) - goto cleanup; - - VIR_DEBUG("Finished reserving existing ports"); - - for (i = 0; i < def->nconsoles; i++) { - virDomainChrDefPtr chr = def->consoles[i]; - if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE && - chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO && - !virDomainVirtioSerialAddrIsComplete(&chr->info) && - virDomainVirtioSerialAddrAutoAssign(def, addrs, &chr->info, true) < 0) - goto cleanup; - } - - for (i = 0; i < def->nchannels; i++) { - virDomainChrDefPtr chr = def->channels[i]; - if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && - chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO && - !virDomainVirtioSerialAddrIsComplete(&chr->info) && - virDomainVirtioSerialAddrAutoAssign(def, addrs, &chr->info, false) < 0) - goto cleanup; - } - - if (obj && obj->privateData) { - priv = obj->privateData; - /* if this is the live domain object, we persist the addresses */ - virDomainVirtioSerialAddrSetFree(priv->vioserialaddrs); - priv->persistentAddrs = 1; - priv->vioserialaddrs = addrs; - addrs = NULL; - } - ret = 0; - - cleanup: - virDomainVirtioSerialAddrSetFree(addrs); - return ret; -} - - -int qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps) -{ - size_t i; - int ret = -1; - int model; - - /* Default values match QEMU. See spapr_(llan|vscsi|vty).c */ - - for (i = 0; i < def->nnets; i++) { - if (def->nets[i]->model && - STREQ(def->nets[i]->model, "spapr-vlan")) - def->nets[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; - if (qemuAssignSpaprVIOAddress(def, &def->nets[i]->info, - VIO_ADDR_NET) < 0) - goto cleanup; - } - - for (i = 0; i < def->ncontrollers; i++) { - model = def->controllers[i]->model; - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { - if (qemuSetSCSIControllerModel(def, qemuCaps, &model) < 0) - goto cleanup; - } - - if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI && - def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) - def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; - if (qemuAssignSpaprVIOAddress(def, &def->controllers[i]->info, - VIO_ADDR_SCSI) < 0) - goto cleanup; - } - - for (i = 0; i < def->nserials; i++) { - if (def->serials[i]->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && - ARCH_IS_PPC64(def->os.arch) && - STRPREFIX(def->os.machine, "pseries")) - def->serials[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; - if (qemuAssignSpaprVIOAddress(def, &def->serials[i]->info, - VIO_ADDR_SERIAL) < 0) - goto cleanup; - } - - if (def->nvram) { - if (ARCH_IS_PPC64(def->os.arch) && - STRPREFIX(def->os.machine, "pseries")) - def->nvram->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; - if (qemuAssignSpaprVIOAddress(def, &def->nvram->info, - VIO_ADDR_NVRAM) < 0) - goto cleanup; - } - - /* No other devices are currently supported on spapr-vio */ - - ret = 0; - - cleanup: - return ret; -} - - -static int -qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainDeviceDefPtr device, - virDomainDeviceInfoPtr info, - void *opaque) -{ - virDomainPCIAddressSetPtr addrs = opaque; - int ret = -1; - virDevicePCIAddressPtr addr = &info->addr.pci; - bool entireSlot; - /* flags may be changed from default below */ - virDomainPCIConnectFlags flags = (VIR_PCI_CONNECT_HOTPLUGGABLE | - VIR_PCI_CONNECT_TYPE_PCI); - - if ((info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) - || ((device->type == VIR_DOMAIN_DEVICE_HOSTDEV) && - (device->data.hostdev->parent.type != VIR_DOMAIN_DEVICE_NONE))) { - /* If a hostdev has a parent, its info will be a part of the - * parent, and will have its address collected during the scan - * of the parent's device type. - */ - return 0; - } - - /* Change flags according to differing requirements of different - * devices. - */ - switch (device->type) { - case VIR_DOMAIN_DEVICE_CONTROLLER: - switch (device->data.controller->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_PCI: - switch (device->data.controller->model) { - case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: - /* pci-bridge needs a PCI slot, but it isn't - * hot-pluggable, so it doesn't need a hot-pluggable slot. - */ - flags = VIR_PCI_CONNECT_TYPE_PCI; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: - /* pci-bridge needs a PCIe slot, but it isn't - * hot-pluggable, so it doesn't need a hot-pluggable slot. - */ - flags = VIR_PCI_CONNECT_TYPE_PCIE; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: - /* pcie-root-port can only connect to pcie-root, isn't - * hot-pluggable - */ - flags = VIR_PCI_CONNECT_TYPE_PCIE_ROOT; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: - /* pcie-switch can only connect to a true - * pcie bus, and can't be hot-plugged. - */ - flags = VIR_PCI_CONNECT_TYPE_PCIE_PORT; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: - /* pcie-switch-downstream-port can only connect to a - * pcie-switch-upstream-port, and can't be hot-plugged. - */ - flags = VIR_PCI_CONNECT_TYPE_PCIE_SWITCH; - break; - default: - break; - } - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_SATA: - /* SATA controllers aren't hot-plugged, and can be put in - * either a PCI or PCIe slot - */ - flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE; - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_USB: - /* allow UHCI and EHCI controllers to be manually placed on - * the PCIe bus (but don't put them there automatically) - */ - switch (device->data.controller->model) { - case VIR_DOMAIN_CONTROLLER_MODEL_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: - case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI: - flags = VIR_PCI_CONNECT_TYPE_PCI; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI: - /* should this be PCIE-only? Or do we need to allow PCI - * for backward compatibility? - */ - flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI: - case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI: - case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI: - /* Allow these for PCI only */ - break; - } - } - break; - - case VIR_DOMAIN_DEVICE_SOUND: - switch (device->data.sound->model) { - case VIR_DOMAIN_SOUND_MODEL_ICH6: - case VIR_DOMAIN_SOUND_MODEL_ICH9: - flags = VIR_PCI_CONNECT_TYPE_PCI; - break; - } - break; - - case VIR_DOMAIN_DEVICE_VIDEO: - /* video cards aren't hot-plugged, and can be put in either a - * PCI or PCIe slot - */ - flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE; - break; - } - - /* Ignore implicit controllers on slot 0:0:1.0: - * implicit IDE controller on 0:0:1.1 (no qemu command line) - * implicit USB controller on 0:0:1.2 (-usb) - * - * If the machine does have a PCI bus, they will get reserved - * in qemuAssignDevicePCISlots(). - */ - - /* These are the IDE and USB controllers in the PIIX3, hardcoded - * to bus 0 slot 1. They cannot be attached to a PCIe slot, only - * PCI. - */ - if (device->type == VIR_DOMAIN_DEVICE_CONTROLLER && addr->domain == 0 && - addr->bus == 0 && addr->slot == 1) { - virDomainControllerDefPtr cont = device->data.controller; - - if ((cont->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && cont->idx == 0 && - addr->function == 1) || - (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && cont->idx == 0 && - (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI || - cont->model == -1) && addr->function == 2)) { - /* Note the check for nbuses > 0 - if there are no PCI - * buses, we skip this check. This is a quirk required for - * some machinetypes such as s390, which pretend to have a - * PCI bus for long enough to generate the "-usb" on the - * commandline, but that don't really care if a PCI bus - * actually exists. */ - if (addrs->nbuses > 0 && - !(addrs->buses[0].flags & VIR_PCI_CONNECT_TYPE_PCI)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Bus 0 must be PCI for integrated PIIX3 " - "USB or IDE controllers")); - return -1; - } else { - return 0; - } - } - } - - entireSlot = (addr->function == 0 && - addr->multi != VIR_TRISTATE_SWITCH_ON); - - if (virDomainPCIAddressReserveAddr(addrs, addr, flags, - entireSlot, true) < 0) - goto cleanup; - - ret = 0; - cleanup: - return ret; -} - -static bool -qemuDomainSupportsPCI(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) -{ - if ((def->os.arch != VIR_ARCH_ARMV7L) && (def->os.arch != VIR_ARCH_AARCH64)) - return true; - - if (STREQ(def->os.machine, "versatilepb")) - return true; - - if ((STREQ(def->os.machine, "virt") || - STRPREFIX(def->os.machine, "virt-")) && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_GPEX)) - return true; - - return false; -} - -static bool -qemuDomainPCIBusFullyReserved(virDomainPCIAddressBusPtr bus) -{ - size_t i; - - for (i = bus->minSlot; i <= bus->maxSlot; i++) - if (!bus->slots[i]) - return false; - - return true; -} - - -static int -qemuAssignDevicePCISlots(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainPCIAddressSetPtr addrs); -static int -qemuDomainAssignPCIAddresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainObjPtr obj); - - -int qemuDomainAssignAddresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainObjPtr obj) -{ - int rc; - - rc = qemuDomainAssignVirtioSerialAddresses(def, obj); - if (rc) - return rc; - - rc = qemuDomainAssignSpaprVIOAddresses(def, qemuCaps); - if (rc) - return rc; - - rc = qemuDomainAssignS390Addresses(def, qemuCaps, obj); - if (rc) - return rc; - - rc = qemuDomainAssignARMVirtioMMIOAddresses(def, qemuCaps); - if (rc) - return rc; - - return qemuDomainAssignPCIAddresses(def, qemuCaps, obj); -} - - -static virDomainPCIAddressSetPtr -qemuDomainPCIAddressSetCreate(virDomainDefPtr def, - unsigned int nbuses, - bool dryRun) -{ - virDomainPCIAddressSetPtr addrs; - size_t i; - - if ((addrs = virDomainPCIAddressSetAlloc(nbuses)) == NULL) - return NULL; - - addrs->nbuses = nbuses; - addrs->dryRun = dryRun; - - /* As a safety measure, set default model='pci-root' for first pci - * controller and 'pci-bridge' for all subsequent. After setting - * those defaults, then scan the config and set the actual model - * for all addrs[idx]->bus that already have a corresponding - * controller in the config. - * - */ - if (nbuses > 0) - virDomainPCIAddressBusSetModel(&addrs->buses[0], - VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT); - for (i = 1; i < nbuses; i++) { - virDomainPCIAddressBusSetModel(&addrs->buses[i], - VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE); - } - - for (i = 0; i < def->ncontrollers; i++) { - size_t idx = def->controllers[i]->idx; - - if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI) - continue; - - if (idx >= addrs->nbuses) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Inappropriate new pci controller index %zu " - "not found in addrs"), idx); - goto error; - } - - if (virDomainPCIAddressBusSetModel(&addrs->buses[idx], - def->controllers[i]->model) < 0) - goto error; - } - - if (virDomainDeviceInfoIterate(def, qemuCollectPCIAddress, addrs) < 0) - goto error; - - return addrs; - - error: - virDomainPCIAddressSetFree(addrs); - return NULL; -} - - -void -qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, - virDomainDeviceInfoPtr info, - const char *devstr) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - - if (!devstr) - devstr = info->alias; - - if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW && - qemuDomainMachineIsS390CCW(vm->def) && - virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_VIRTIO_CCW) && - virDomainCCWAddressReleaseAddr(priv->ccwaddrs, info) < 0) - VIR_WARN("Unable to release CCW address on %s", - NULLSTR(devstr)); - else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && - virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && - virDomainPCIAddressReleaseSlot(priv->pciaddrs, - &info->addr.pci) < 0) - VIR_WARN("Unable to release PCI address on %s", - NULLSTR(devstr)); - if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL && - virDomainVirtioSerialAddrRelease(priv->vioserialaddrs, info) < 0) - VIR_WARN("Unable to release virtio-serial address on %s", - NULLSTR(devstr)); -} - - -#define IS_USB2_CONTROLLER(ctrl) \ - (((ctrl)->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) && \ - ((ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1 || \ - (ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1 || \ - (ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2 || \ - (ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3)) - - -static int -qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainPCIAddressSetPtr addrs) -{ - int ret = -1; - size_t i; - virDevicePCIAddress tmp_addr; - bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); - char *addrStr = NULL; - virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI; - - /* Verify that first IDE and USB controllers (if any) is on the PIIX3, fn 1 */ - for (i = 0; i < def->ncontrollers; i++) { - /* First IDE controller lives on the PIIX3 at slot=1, function=1 */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && - def->controllers[i]->idx == 0) { - if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - if (def->controllers[i]->info.addr.pci.domain != 0 || - def->controllers[i]->info.addr.pci.bus != 0 || - def->controllers[i]->info.addr.pci.slot != 1 || - def->controllers[i]->info.addr.pci.function != 1) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Primary IDE controller must have PCI address 0:0:1.1")); - goto cleanup; - } - } else { - def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->controllers[i]->info.addr.pci.domain = 0; - def->controllers[i]->info.addr.pci.bus = 0; - def->controllers[i]->info.addr.pci.slot = 1; - def->controllers[i]->info.addr.pci.function = 1; - } - } else if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && - def->controllers[i]->idx == 0 && - (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI || - def->controllers[i]->model == -1)) { - if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - if (def->controllers[i]->info.addr.pci.domain != 0 || - def->controllers[i]->info.addr.pci.bus != 0 || - def->controllers[i]->info.addr.pci.slot != 1 || - def->controllers[i]->info.addr.pci.function != 2) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("PIIX3 USB controller must have PCI address 0:0:1.2")); - goto cleanup; - } - } else { - def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->controllers[i]->info.addr.pci.domain = 0; - def->controllers[i]->info.addr.pci.bus = 0; - def->controllers[i]->info.addr.pci.slot = 1; - def->controllers[i]->info.addr.pci.function = 2; - } - } - } - - /* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) - * hardcoded slot=1, multifunction device - */ - if (addrs->nbuses) { - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 1; - if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) - goto cleanup; - } - - if (def->nvideos > 0) { - /* Because the PIIX3 integrated IDE/USB controllers are - * already at slot 1, when qemu looks for the first free slot - * to place the VGA controller (which is always the first - * device added after integrated devices), it *always* ends up - * at slot 2. - */ - virDomainVideoDefPtr primaryVideo = def->videos[0]; - if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 2; - - if (!(addrStr = virDomainPCIAddressAsString(&tmp_addr))) - goto cleanup; - if (!virDomainPCIAddressValidate(addrs, &tmp_addr, - addrStr, flags, false)) - goto cleanup; - - if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { - if (qemuDeviceVideoUsable) { - if (virDomainPCIAddressReserveNextSlot(addrs, - &primaryVideo->info, - flags) < 0) - goto cleanup; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("PCI address 0:0:2.0 is in use, " - "QEMU needs it for primary video")); - goto cleanup; - } - } else { - if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) - goto cleanup; - primaryVideo->info.addr.pci = tmp_addr; - primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - } - } else if (!qemuDeviceVideoUsable) { - if (primaryVideo->info.addr.pci.domain != 0 || - primaryVideo->info.addr.pci.bus != 0 || - primaryVideo->info.addr.pci.slot != 2 || - primaryVideo->info.addr.pci.function != 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Primary video card must have PCI address 0:0:2.0")); - goto cleanup; - } - /* If TYPE == PCI, then qemuCollectPCIAddress() function - * has already reserved the address, so we must skip */ - } - } else if (addrs->nbuses && !qemuDeviceVideoUsable) { - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 2; - - if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { - VIR_DEBUG("PCI address 0:0:2.0 in use, future addition of a video" - " device will not be possible without manual" - " intervention"); - } else if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) { - goto cleanup; - } - } - ret = 0; - cleanup: - VIR_FREE(addrStr); - return ret; -} - - -static int -qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainPCIAddressSetPtr addrs) -{ - int ret = -1; - size_t i; - virDevicePCIAddress tmp_addr; - bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); - char *addrStr = NULL; - virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPE_PCIE; - - for (i = 0; i < def->ncontrollers; i++) { - switch (def->controllers[i]->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SATA: - /* Verify that the first SATA controller is at 00:1F.2 the - * q35 machine type *always* has a SATA controller at this - * address. - */ - if (def->controllers[i]->idx == 0) { - if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - if (def->controllers[i]->info.addr.pci.domain != 0 || - def->controllers[i]->info.addr.pci.bus != 0 || - def->controllers[i]->info.addr.pci.slot != 0x1F || - def->controllers[i]->info.addr.pci.function != 2) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Primary SATA controller must have PCI address 0:0:1f.2")); - goto cleanup; - } - } else { - def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->controllers[i]->info.addr.pci.domain = 0; - def->controllers[i]->info.addr.pci.bus = 0; - def->controllers[i]->info.addr.pci.slot = 0x1F; - def->controllers[i]->info.addr.pci.function = 2; - } - } - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_USB: - if ((def->controllers[i]->model - == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1) && - (def->controllers[i]->info.type - == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)) { - /* Try to assign the first found USB2 controller to - * 00:1D.0 and 2nd to 00:1A.0 (because that is their - * standard location on real Q35 hardware) unless they - * are already taken, but don't insist on it. - * - * (NB: all other controllers at the same index will - * get assigned to the same slot as the UHCI1 when - * addresses are later assigned to all devices.) - */ - bool assign = false; - - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 0x1D; - if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { - assign = true; - } else { - tmp_addr.slot = 0x1A; - if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) - assign = true; - } - if (assign) { - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, - flags, false, true) < 0) - goto cleanup; - def->controllers[i]->info.type - = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->controllers[i]->info.addr.pci.domain = 0; - def->controllers[i]->info.addr.pci.bus = 0; - def->controllers[i]->info.addr.pci.slot = tmp_addr.slot; - def->controllers[i]->info.addr.pci.function = 0; - def->controllers[i]->info.addr.pci.multi - = VIR_TRISTATE_SWITCH_ON; - } - } - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_PCI: - if (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE && - def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - /* Try to assign this bridge to 00:1E.0 (because that - * is its standard location on real hardware) unless - * it's already taken, but don't insist on it. - */ - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 0x1E; - if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, - flags, true, false) < 0) - goto cleanup; - def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->controllers[i]->info.addr.pci.domain = 0; - def->controllers[i]->info.addr.pci.bus = 0; - def->controllers[i]->info.addr.pci.slot = 0x1E; - def->controllers[i]->info.addr.pci.function = 0; - } - } - break; - } - } - - /* Reserve slot 0x1F function 0 (ISA bridge, not in config model) - * and function 3 (SMBus, also not (yet) in config model). As with - * the SATA controller, these devices are always present in a q35 - * machine; there is no way to not have them. - */ - if (addrs->nbuses) { - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 0x1F; - tmp_addr.function = 0; - tmp_addr.multi = VIR_TRISTATE_SWITCH_ON; - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, - false, false) < 0) - goto cleanup; - tmp_addr.function = 3; - tmp_addr.multi = VIR_TRISTATE_SWITCH_ABSENT; - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, - false, false) < 0) - goto cleanup; - } - - if (def->nvideos > 0) { - /* NB: unlike the pc machinetypes, on q35 machinetypes the - * integrated devices are at slot 0x1f, so when qemu looks for - * the first free lot for the first VGA, it will always be at - * slot 1 (which was used up by the integrated PIIX3 devices - * on pc machinetypes). - */ - virDomainVideoDefPtr primaryVideo = def->videos[0]; - if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 1; - - if (!(addrStr = virDomainPCIAddressAsString(&tmp_addr))) - goto cleanup; - if (!virDomainPCIAddressValidate(addrs, &tmp_addr, - addrStr, flags, false)) - goto cleanup; - - if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { - if (qemuDeviceVideoUsable) { - if (virDomainPCIAddressReserveNextSlot(addrs, - &primaryVideo->info, - flags) < 0) - goto cleanup; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("PCI address 0:0:1.0 is in use, " - "QEMU needs it for primary video")); - goto cleanup; - } - } else { - if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) - goto cleanup; - primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - primaryVideo->info.addr.pci = tmp_addr; - } - } else if (!qemuDeviceVideoUsable) { - if (primaryVideo->info.addr.pci.domain != 0 || - primaryVideo->info.addr.pci.bus != 0 || - primaryVideo->info.addr.pci.slot != 1 || - primaryVideo->info.addr.pci.function != 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Primary video card must have PCI address 0:0:1.0")); - goto cleanup; - } - /* If TYPE == PCI, then qemuCollectPCIAddress() function - * has already reserved the address, so we must skip */ - } - } else if (addrs->nbuses && !qemuDeviceVideoUsable) { - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 1; - - if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { - VIR_DEBUG("PCI address 0:0:1.0 in use, future addition of a video" - " device will not be possible without manual" - " intervention"); - virResetLastError(); - } else if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) { - goto cleanup; - } - } - ret = 0; - cleanup: - VIR_FREE(addrStr); - return ret; -} - -static int -qemuValidateDevicePCISlotsChipsets(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainPCIAddressSetPtr addrs) -{ - if (qemuDomainMachineIsI440FX(def) && - qemuValidateDevicePCISlotsPIIX3(def, qemuCaps, addrs) < 0) { - return -1; - } - - if (qemuDomainMachineIsQ35(def) && - qemuDomainValidateDevicePCISlotsQ35(def, qemuCaps, addrs) < 0) { - return -1; - } - - return 0; -} - -static int -qemuDomainAssignPCIAddresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainObjPtr obj) -{ - int ret = -1; - virDomainPCIAddressSetPtr addrs = NULL; - qemuDomainObjPrivatePtr priv = NULL; - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { - int max_idx = -1; - int nbuses = 0; - size_t i; - int rv; - bool buses_reserved = true; - - virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPE_PCI; - - for (i = 0; i < def->ncontrollers; i++) { - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { - if ((int) def->controllers[i]->idx > max_idx) - max_idx = def->controllers[i]->idx; - } - } - - nbuses = max_idx + 1; - - if (nbuses > 0 && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) { - virDomainDeviceInfo info; - - /* 1st pass to figure out how many PCI bridges we need */ - if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, true))) - goto cleanup; - - if (qemuValidateDevicePCISlotsChipsets(def, qemuCaps, addrs) < 0) - goto cleanup; - - for (i = 0; i < addrs->nbuses; i++) { - if (!qemuDomainPCIBusFullyReserved(&addrs->buses[i])) - buses_reserved = false; - } - - /* Reserve 1 extra slot for a (potential) bridge only if buses - * are not fully reserved yet - */ - if (!buses_reserved && - virDomainPCIAddressReserveNextSlot(addrs, &info, flags) < 0) - goto cleanup; - - if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0) - goto cleanup; - - for (i = 1; i < addrs->nbuses; i++) { - virDomainPCIAddressBusPtr bus = &addrs->buses[i]; - - if ((rv = virDomainDefMaybeAddController( - def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, - i, bus->model)) < 0) - goto cleanup; - /* If we added a new bridge, we will need one more address */ - if (rv > 0 && - virDomainPCIAddressReserveNextSlot(addrs, &info, flags) < 0) - goto cleanup; - } - nbuses = addrs->nbuses; - virDomainPCIAddressSetFree(addrs); - addrs = NULL; - - } else if (max_idx > 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("PCI bridges are not supported " - "by this QEMU binary")); - goto cleanup; - } - - if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, false))) - goto cleanup; - - if (qemuDomainSupportsPCI(def, qemuCaps)) { - if (qemuValidateDevicePCISlotsChipsets(def, qemuCaps, addrs) < 0) - goto cleanup; - - if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0) - goto cleanup; - - for (i = 0; i < def->ncontrollers; i++) { - virDomainControllerDefPtr cont = def->controllers[i]; - int idx = cont->idx; - virDevicePCIAddressPtr addr; - virDomainPCIControllerOptsPtr options; - - if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI) - continue; - - addr = &cont->info.addr.pci; - options = &cont->opts.pciopts; - - /* set defaults for any other auto-generated config - * options for this controller that haven't been - * specified in config. - */ - switch ((virDomainControllerModelPCI)cont->model) { - case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: - if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) - options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE; - if (options->chassisNr == -1) - options->chassisNr = cont->idx; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: - if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) - options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: - if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) - options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420; - if (options->chassis == -1) - options->chassis = cont->idx; - if (options->port == -1) - options->port = (addr->slot << 3) + addr->function; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: - if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) - options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: - if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) - options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM; - if (options->chassis == -1) - options->chassis = cont->idx; - if (options->port == -1) - options->port = addr->slot; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT: - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT: - case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST: - break; - } - - /* check if every PCI bridge controller's ID is greater than - * the bus it is placed onto - */ - if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE && - idx <= addr->bus) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("failed to create PCI bridge " - "on bus %d: too many devices with fixed " - "addresses"), - addr->bus); - goto cleanup; - } - } - } - } - - if (obj && obj->privateData) { - priv = obj->privateData; - if (addrs) { - /* if this is the live domain object, we persist the PCI addresses*/ - virDomainPCIAddressSetFree(priv->pciaddrs); - priv->persistentAddrs = 1; - priv->pciaddrs = addrs; - addrs = NULL; - } else { - priv->persistentAddrs = 0; - } - } - - ret = 0; - - cleanup: - virDomainPCIAddressSetFree(addrs); - - return ret; -} - - -/* - * This assigns static PCI slots to all configured devices. - * The ordering here is chosen to match the ordering used - * with old QEMU < 0.12, so that if a user updates a QEMU - * host from old QEMU to QEMU >= 0.12, their guests should - * get PCI addresses in the same order as before. - * - * NB, if they previously hotplugged devices then all bets - * are off. Hotplug for old QEMU was unfixably broken wrt - * to stable PCI addressing. - * - * Order is: - * - * - Host bridge (slot 0) - * - PIIX3 ISA bridge, IDE controller, something else unknown, USB controller (slot 1) - * - Video (slot 2) - * - * - These integrated devices were already added by - * qemuValidateDevicePCISlotsChipsets invoked right before this function - * - * Incrementally assign slots from 3 onwards: - * - * - Net - * - Sound - * - SCSI controllers - * - VirtIO block - * - VirtIO balloon - * - Host device passthrough - * - Watchdog - * - pci serial devices - * - * Prior to this function being invoked, qemuCollectPCIAddress() will have - * added all existing PCI addresses from the 'def' to 'addrs'. Thus this - * function must only try to reserve addresses if info.type == NONE and - * skip over info.type == PCI - */ -static int -qemuAssignDevicePCISlots(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainPCIAddressSetPtr addrs) -{ - size_t i, j; - virDomainPCIConnectFlags flags; - virDevicePCIAddress tmp_addr; - - /* PCI controllers */ - for (i = 0; i < def->ncontrollers; i++) { - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { - if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - switch (def->controllers[i]->model) { - case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT: - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT: - /* pci-root and pcie-root are implicit in the machine, - * and needs no address */ - continue; - case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: - /* pci-bridge doesn't require hot-plug - * (although it does provide hot-plug in its slots) - */ - flags = VIR_PCI_CONNECT_TYPE_PCI; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: - /* dmi-to-pci-bridge requires a non-hotplug PCIe - * slot - */ - flags = VIR_PCI_CONNECT_TYPE_PCIE; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: - /* pcie-root-port can only plug into pcie-root */ - flags = VIR_PCI_CONNECT_TYPE_PCIE_ROOT; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: - /* pcie-switch really does need a real PCIe - * port, but it doesn't need to be pcie-root - */ - flags = VIR_PCI_CONNECT_TYPE_PCIE_PORT; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: - /* pcie-switch-port can only plug into pcie-switch */ - flags = VIR_PCI_CONNECT_TYPE_PCIE_SWITCH; - break; - default: - flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI; - break; - } - if (virDomainPCIAddressReserveNextSlot(addrs, - &def->controllers[i]->info, - flags) < 0) - goto error; - } - } - - flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI; - - for (i = 0; i < def->nfss; i++) { - if (def->fss[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - /* Only support VirtIO-9p-pci so far. If that changes, - * we might need to skip devices here */ - if (virDomainPCIAddressReserveNextSlot(addrs, &def->fss[i]->info, - flags) < 0) - goto error; - } - - /* Network interfaces */ - for (i = 0; i < def->nnets; i++) { - /* type='hostdev' network devices might be USB, and are also - * in hostdevs list anyway, so handle them with other hostdevs - * instead of here. - */ - if ((def->nets[i]->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) || - (def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)) { - continue; - } - if (virDomainPCIAddressReserveNextSlot(addrs, &def->nets[i]->info, - flags) < 0) - goto error; - } - - /* Sound cards */ - for (i = 0; i < def->nsounds; i++) { - if (def->sounds[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - /* Skip ISA sound card, PCSPK and usb-audio */ - if (def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_SB16 || - def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_PCSPK || - def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_USB) - continue; - - if (virDomainPCIAddressReserveNextSlot(addrs, &def->sounds[i]->info, - flags) < 0) - goto error; - } - - /* Device controllers (SCSI, USB, but not IDE, FDC or CCID) */ - for (i = 0; i < def->ncontrollers; i++) { - /* PCI controllers have been dealt with earlier */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) - continue; - - /* USB controller model 'none' doesn't need a PCI address */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && - def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) - continue; - - /* FDC lives behind the ISA bridge; CCID is a usb device */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC || - def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID) - continue; - - /* First IDE controller lives on the PIIX3 at slot=1, function=1, - dealt with earlier on*/ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && - def->controllers[i]->idx == 0) - continue; - - if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - /* USB2 needs special handling to put all companions in the same slot */ - if (IS_USB2_CONTROLLER(def->controllers[i])) { - virDevicePCIAddress addr = { 0, 0, 0, 0, false }; - bool foundAddr = false; - - memset(&tmp_addr, 0, sizeof(tmp_addr)); - for (j = 0; j < def->ncontrollers; j++) { - if (IS_USB2_CONTROLLER(def->controllers[j]) && - def->controllers[j]->idx == def->controllers[i]->idx && - def->controllers[j]->info.type - == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - addr = def->controllers[j]->info.addr.pci; - foundAddr = true; - break; - } - } - - switch (def->controllers[i]->model) { - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1: - addr.function = 7; - addr.multi = VIR_TRISTATE_SWITCH_ABSENT; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1: - addr.function = 0; - addr.multi = VIR_TRISTATE_SWITCH_ON; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2: - addr.function = 1; - addr.multi = VIR_TRISTATE_SWITCH_ABSENT; - break; - case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3: - addr.function = 2; - addr.multi = VIR_TRISTATE_SWITCH_ABSENT; - break; - } - - if (!foundAddr) { - /* This is the first part of the controller, so need - * to find a free slot & then reserve a function */ - if (virDomainPCIAddressGetNextSlot(addrs, &tmp_addr, flags) < 0) - goto error; - - addr.bus = tmp_addr.bus; - addr.slot = tmp_addr.slot; - - addrs->lastaddr = addr; - addrs->lastaddr.function = 0; - addrs->lastaddr.multi = VIR_TRISTATE_SWITCH_ABSENT; - } - /* Finally we can reserve the slot+function */ - if (virDomainPCIAddressReserveAddr(addrs, &addr, flags, - false, foundAddr) < 0) - goto error; - - def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->controllers[i]->info.addr.pci = addr; - } else { - if (virDomainPCIAddressReserveNextSlot(addrs, - &def->controllers[i]->info, - flags) < 0) - goto error; - } - } - - /* Disks (VirtIO only for now) */ - for (i = 0; i < def->ndisks; i++) { - /* Only VirtIO disks use PCI addrs */ - if (def->disks[i]->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) - continue; - - /* don't touch s390 devices */ - if (def->disks[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI || - def->disks[i]->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390 || - def->disks[i]->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) - continue; - - /* Also ignore virtio-mmio disks if our machine allows them */ - if (def->disks[i]->info.type == - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_MMIO)) - continue; - - if (def->disks[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("virtio disk cannot have an address of type '%s'"), - virDomainDeviceAddressTypeToString(def->disks[i]->info.type)); - goto error; - } - - if (virDomainPCIAddressReserveNextSlot(addrs, &def->disks[i]->info, - flags) < 0) - goto error; - } - - /* Host PCI devices */ - for (i = 0; i < def->nhostdevs; i++) { - if (def->hostdevs[i]->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) - continue; - - if (virDomainPCIAddressReserveNextSlot(addrs, - def->hostdevs[i]->info, - flags) < 0) - goto error; - } - - /* VirtIO balloon */ - if (def->memballoon && - def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO && - def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - if (virDomainPCIAddressReserveNextSlot(addrs, - &def->memballoon->info, - flags) < 0) - goto error; - } - - /* VirtIO RNG */ - for (i = 0; i < def->nrngs; i++) { - if (def->rngs[i]->model != VIR_DOMAIN_RNG_MODEL_VIRTIO || - def->rngs[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - if (virDomainPCIAddressReserveNextSlot(addrs, - &def->rngs[i]->info, flags) < 0) - goto error; - } - - /* A watchdog - check if it is a PCI device */ - if (def->watchdog && - def->watchdog->model == VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB && - def->watchdog->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - if (virDomainPCIAddressReserveNextSlot(addrs, &def->watchdog->info, - flags) < 0) - goto error; - } - - /* Assign a PCI slot to the primary video card if there is not an - * assigned address. */ - if (def->nvideos > 0 && - def->videos[0]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - if (virDomainPCIAddressReserveNextSlot(addrs, &def->videos[0]->info, - flags) < 0) - goto error; - } - - /* Further non-primary video cards which have to be qxl type */ - for (i = 1; i < def->nvideos; i++) { - if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("non-primary video device must be type of 'qxl'")); - goto error; - } - if (def->videos[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - if (virDomainPCIAddressReserveNextSlot(addrs, &def->videos[i]->info, - flags) < 0) - goto error; - } - - /* Shared Memory */ - for (i = 0; i < def->nshmems; i++) { - if (def->shmems[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - if (virDomainPCIAddressReserveNextSlot(addrs, - &def->shmems[i]->info, flags) < 0) - goto error; - } - for (i = 0; i < def->ninputs; i++) { - if (def->inputs[i]->bus != VIR_DOMAIN_INPUT_BUS_VIRTIO) - continue; - if (def->inputs[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - if (virDomainPCIAddressReserveNextSlot(addrs, - &def->inputs[i]->info, flags) < 0) - goto error; - } - for (i = 0; i < def->nparallels; i++) { - /* Nada - none are PCI based (yet) */ - } - for (i = 0; i < def->nserials; i++) { - virDomainChrDefPtr chr = def->serials[i]; - - if (chr->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI) - continue; - - if (chr->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - if (virDomainPCIAddressReserveNextSlot(addrs, &chr->info, flags) < 0) - goto error; - } - for (i = 0; i < def->nchannels; i++) { - /* Nada - none are PCI based (yet) */ - } - for (i = 0; i < def->nhubs; i++) { - /* Nada - none are PCI based (yet) */ - } - - return 0; - - error: - return -1; -} - static int qemuBuildDeviceAddressStr(virBufferPtr buf, virDomainDefPtr domainDef, @@ -3741,7 +2155,8 @@ qemuBuildDriveDevStr(virDomainDefPtr def, controllerModel = virDomainDeviceFindControllerModel(def, &disk->info, VIR_DOMAIN_CONTROLLER_TYPE_SCSI); - if ((qemuSetSCSIControllerModel(def, qemuCaps, &controllerModel)) < 0) + if ((qemuDomainSetSCSIControllerModel(def, qemuCaps, + &controllerModel)) < 0) goto error; if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { @@ -4131,7 +2546,7 @@ qemuBuildControllerDevStr(virDomainDefPtr domainDef, return NULL; if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { - if ((qemuSetSCSIControllerModel(domainDef, qemuCaps, &model)) < 0) + if ((qemuDomainSetSCSIControllerModel(domainDef, qemuCaps, &model)) < 0) return NULL; } @@ -5965,7 +4380,7 @@ qemuBuildSCSIHostdevDevStr(virDomainDefPtr def, model = virDomainDeviceFindControllerModel(def, dev->info, VIR_DOMAIN_CONTROLLER_TYPE_SCSI); - if (qemuSetSCSIControllerModel(def, qemuCaps, &model) < 0) + if (qemuDomainSetSCSIControllerModel(def, qemuCaps, &model) < 0) goto error; if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) { diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 5fb91e5..e523054 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -232,18 +232,6 @@ int qemuOpenVhostNet(virDomainDefPtr def, int qemuNetworkPrepareDevices(virDomainDefPtr def); -int qemuDomainAssignAddresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, - virDomainObjPtr obj) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); -int qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def, - virQEMUCapsPtr qemuCaps); - -void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, - virDomainDeviceInfoPtr info, - const char *devstr); - - int qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps); int qemuDomainNetVLAN(virDomainNetDefPtr def); int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 686c9e4..0616574 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -56,6 +56,12 @@ VIR_LOG_INIT("qemu.qemu_domain"); +#define VIO_ADDR_NET 0x1000ul +#define VIO_ADDR_SCSI 0x2000ul +#define VIO_ADDR_SERIAL 0x30000000ul +#define VIO_ADDR_NVRAM 0x3000ul + + #define QEMU_NAMESPACE_HREF "http://libvirt.org/schemas/domain/qemu/1.0" VIR_ENUM_IMPL(qemuDomainJob, QEMU_JOB_LAST, @@ -4395,3 +4401,1589 @@ qemuDomainSupportsNetdev(virDomainDefPtr def, return false; return virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV); } + + +int +qemuDomainSetSCSIControllerModel(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + int *model) +{ + if (*model > 0) { + switch (*model) { + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU doesn't support " + "the LSI 53C895A SCSI controller")); + return -1; + } + break; + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU doesn't support " + "virtio scsi controller")); + return -1; + } + break; + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI: + /*TODO: need checking work here if necessary */ + break; + case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_MEGASAS)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU doesn't support " + "the LSI SAS1078 controller")); + return -1; + } + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported controller model: %s"), + virDomainControllerModelSCSITypeToString(*model)); + return -1; + } + } else { + if (ARCH_IS_PPC64(def->os.arch) && + STRPREFIX(def->os.machine, "pseries")) { + *model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI; + } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI)) { + *model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC; + } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI)) { + *model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine model for scsi controller")); + return -1; + } + } + + return 0; +} + + +static int +qemuDomainAssignVirtioSerialAddresses(virDomainDefPtr def, + virDomainObjPtr obj) +{ + int ret = -1; + size_t i; + virDomainVirtioSerialAddrSetPtr addrs = NULL; + qemuDomainObjPrivatePtr priv = NULL; + + if (!(addrs = virDomainVirtioSerialAddrSetCreate())) + goto cleanup; + + if (virDomainVirtioSerialAddrSetAddControllers(addrs, def) < 0) + goto cleanup; + + if (virDomainDeviceInfoIterate(def, virDomainVirtioSerialAddrReserve, + addrs) < 0) + goto cleanup; + + VIR_DEBUG("Finished reserving existing ports"); + + for (i = 0; i < def->nconsoles; i++) { + virDomainChrDefPtr chr = def->consoles[i]; + if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE && + chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO && + !virDomainVirtioSerialAddrIsComplete(&chr->info) && + virDomainVirtioSerialAddrAutoAssign(def, addrs, &chr->info, true) < 0) + goto cleanup; + } + + for (i = 0; i < def->nchannels; i++) { + virDomainChrDefPtr chr = def->channels[i]; + if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && + chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO && + !virDomainVirtioSerialAddrIsComplete(&chr->info) && + virDomainVirtioSerialAddrAutoAssign(def, addrs, &chr->info, false) < 0) + goto cleanup; + } + + if (obj && obj->privateData) { + priv = obj->privateData; + /* if this is the live domain object, we persist the addresses */ + virDomainVirtioSerialAddrSetFree(priv->vioserialaddrs); + priv->persistentAddrs = 1; + priv->vioserialaddrs = addrs; + addrs = NULL; + } + ret = 0; + + cleanup: + virDomainVirtioSerialAddrSetFree(addrs); + return ret; +} + + +static int +qemuDomainSpaprVIOFindByReg(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr info, void *opaque) +{ + virDomainDeviceInfoPtr target = opaque; + + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) + return 0; + + /* Match a dev that has a reg, is not us, and has a matching reg */ + if (info->addr.spaprvio.has_reg && info != target && + info->addr.spaprvio.reg == target->addr.spaprvio.reg) + /* Has to be < 0 so virDomainDeviceInfoIterate() will exit */ + return -1; + + return 0; +} + + +static int +qemuDomainAssignSpaprVIOAddress(virDomainDefPtr def, + virDomainDeviceInfoPtr info, + unsigned long long default_reg) +{ + bool user_reg; + int ret; + + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) + return 0; + + /* Check if the user has assigned the reg already, if so use it */ + user_reg = info->addr.spaprvio.has_reg; + if (!user_reg) { + info->addr.spaprvio.reg = default_reg; + info->addr.spaprvio.has_reg = true; + } + + ret = virDomainDeviceInfoIterate(def, qemuDomainSpaprVIOFindByReg, info); + while (ret != 0) { + if (user_reg) { + virReportError(VIR_ERR_XML_ERROR, + _("spapr-vio address %#llx already in use"), + info->addr.spaprvio.reg); + return -EEXIST; + } + + /* We assigned the reg, so try a new value */ + info->addr.spaprvio.reg += 0x1000; + ret = virDomainDeviceInfoIterate(def, qemuDomainSpaprVIOFindByReg, + info); + } + + return 0; +} + + +static int +qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps) +{ + size_t i; + int ret = -1; + int model; + + /* Default values match QEMU. See spapr_(llan|vscsi|vty).c */ + + for (i = 0; i < def->nnets; i++) { + if (def->nets[i]->model && + STREQ(def->nets[i]->model, "spapr-vlan")) + def->nets[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; + if (qemuDomainAssignSpaprVIOAddress(def, &def->nets[i]->info, + VIO_ADDR_NET) < 0) + goto cleanup; + } + + for (i = 0; i < def->ncontrollers; i++) { + model = def->controllers[i]->model; + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { + if (qemuDomainSetSCSIControllerModel(def, qemuCaps, &model) < 0) + goto cleanup; + } + + if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI && + def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; + if (qemuDomainAssignSpaprVIOAddress(def, &def->controllers[i]->info, + VIO_ADDR_SCSI) < 0) + goto cleanup; + } + + for (i = 0; i < def->nserials; i++) { + if (def->serials[i]->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && + ARCH_IS_PPC64(def->os.arch) && + STRPREFIX(def->os.machine, "pseries")) + def->serials[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; + if (qemuDomainAssignSpaprVIOAddress(def, &def->serials[i]->info, + VIO_ADDR_SERIAL) < 0) + goto cleanup; + } + + if (def->nvram) { + if (ARCH_IS_PPC64(def->os.arch) && + STRPREFIX(def->os.machine, "pseries")) + def->nvram->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO; + if (qemuDomainAssignSpaprVIOAddress(def, &def->nvram->info, + VIO_ADDR_NVRAM) < 0) + goto cleanup; + } + + /* No other devices are currently supported on spapr-vio */ + + ret = 0; + + cleanup: + return ret; +} + + +static void +qemuDomainPrimeVirtioDeviceAddresses(virDomainDefPtr def, + virDomainDeviceAddressType type) +{ + /* + declare address-less virtio devices to be of address type 'type' + disks, networks, consoles, controllers, memballoon and rng in this + order + if type is ccw filesystem devices are declared to be of address type ccw + */ + size_t i; + + for (i = 0; i < def->ndisks; i++) { + if (def->disks[i]->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && + def->disks[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + def->disks[i]->info.type = type; + } + + for (i = 0; i < def->nnets; i++) { + if (STREQ(def->nets[i]->model, "virtio") && + def->nets[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + def->nets[i]->info.type = type; + } + } + + for (i = 0; i < def->ninputs; i++) { + if (def->inputs[i]->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && + def->inputs[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + def->inputs[i]->info.type = type; + } + + for (i = 0; i < def->ncontrollers; i++) { + if ((def->controllers[i]->type == + VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL || + def->controllers[i]->type == + VIR_DOMAIN_CONTROLLER_TYPE_SCSI) && + def->controllers[i]->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + def->controllers[i]->info.type = type; + } + + if (def->memballoon && + def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO && + def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + def->memballoon->info.type = type; + + for (i = 0; i < def->nrngs; i++) { + if (def->rngs[i]->model == VIR_DOMAIN_RNG_MODEL_VIRTIO && + def->rngs[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + def->rngs[i]->info.type = type; + } + + if (type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { + for (i = 0; i < def->nfss; i++) { + if (def->fss[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + def->fss[i]->info.type = type; + } + } +} + + +/* + * Three steps populating CCW devnos + * 1. Allocate empty address set + * 2. Gather addresses with explicit devno + * 3. Assign defaults to the rest + */ +static int +qemuDomainAssignS390Addresses(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainObjPtr obj) +{ + int ret = -1; + virDomainCCWAddressSetPtr addrs = NULL; + qemuDomainObjPrivatePtr priv = NULL; + + if (qemuDomainMachineIsS390CCW(def) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) { + qemuDomainPrimeVirtioDeviceAddresses( + def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW); + + if (!(addrs = virDomainCCWAddressSetCreate())) + goto cleanup; + + if (virDomainDeviceInfoIterate(def, virDomainCCWAddressValidate, + addrs) < 0) + goto cleanup; + + if (virDomainDeviceInfoIterate(def, virDomainCCWAddressAllocate, + addrs) < 0) + goto cleanup; + } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) { + /* deal with legacy virtio-s390 */ + qemuDomainPrimeVirtioDeviceAddresses( + def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390); + } + + if (obj && obj->privateData) { + priv = obj->privateData; + if (addrs) { + /* if this is the live domain object, we persist the CCW addresses*/ + virDomainCCWAddressSetFree(priv->ccwaddrs); + priv->persistentAddrs = 1; + priv->ccwaddrs = addrs; + addrs = NULL; + } else { + priv->persistentAddrs = 0; + } + } + ret = 0; + + cleanup: + virDomainCCWAddressSetFree(addrs); + + return ret; +} + + +static int +qemuDomainAssignARMVirtioMMIOAddresses(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps) +{ + if (((def->os.arch == VIR_ARCH_ARMV7L) || + (def->os.arch == VIR_ARCH_AARCH64)) && + (STRPREFIX(def->os.machine, "vexpress-") || + STREQ(def->os.machine, "virt") || + STRPREFIX(def->os.machine, "virt-")) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_MMIO)) { + qemuDomainPrimeVirtioDeviceAddresses( + def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO); + } + return 0; +} + + +static int +qemuDomainCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr device, + virDomainDeviceInfoPtr info, + void *opaque) +{ + virDomainPCIAddressSetPtr addrs = opaque; + int ret = -1; + virDevicePCIAddressPtr addr = &info->addr.pci; + bool entireSlot; + /* flags may be changed from default below */ + virDomainPCIConnectFlags flags = (VIR_PCI_CONNECT_HOTPLUGGABLE | + VIR_PCI_CONNECT_TYPE_PCI); + + if ((info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + || ((device->type == VIR_DOMAIN_DEVICE_HOSTDEV) && + (device->data.hostdev->parent.type != VIR_DOMAIN_DEVICE_NONE))) { + /* If a hostdev has a parent, its info will be a part of the + * parent, and will have its address collected during the scan + * of the parent's device type. + */ + return 0; + } + + /* Change flags according to differing requirements of different + * devices. + */ + switch (device->type) { + case VIR_DOMAIN_DEVICE_CONTROLLER: + switch (device->data.controller->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_PCI: + switch (device->data.controller->model) { + case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: + /* pci-bridge needs a PCI slot, but it isn't + * hot-pluggable, so it doesn't need a hot-pluggable slot. + */ + flags = VIR_PCI_CONNECT_TYPE_PCI; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: + /* pci-bridge needs a PCIe slot, but it isn't + * hot-pluggable, so it doesn't need a hot-pluggable slot. + */ + flags = VIR_PCI_CONNECT_TYPE_PCIE; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: + /* pcie-root-port can only connect to pcie-root, isn't + * hot-pluggable + */ + flags = VIR_PCI_CONNECT_TYPE_PCIE_ROOT; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: + /* pcie-switch can only connect to a true + * pcie bus, and can't be hot-plugged. + */ + flags = VIR_PCI_CONNECT_TYPE_PCIE_PORT; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: + /* pcie-switch-downstream-port can only connect to a + * pcie-switch-upstream-port, and can't be hot-plugged. + */ + flags = VIR_PCI_CONNECT_TYPE_PCIE_SWITCH; + break; + default: + break; + } + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_SATA: + /* SATA controllers aren't hot-plugged, and can be put in + * either a PCI or PCIe slot + */ + flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE; + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_USB: + /* allow UHCI and EHCI controllers to be manually placed on + * the PCIe bus (but don't put them there automatically) + */ + switch (device->data.controller->model) { + case VIR_DOMAIN_CONTROLLER_MODEL_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: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI: + flags = VIR_PCI_CONNECT_TYPE_PCI; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI: + /* should this be PCIE-only? Or do we need to allow PCI + * for backward compatibility? + */ + flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI: + /* Allow these for PCI only */ + break; + } + } + break; + + case VIR_DOMAIN_DEVICE_SOUND: + switch (device->data.sound->model) { + case VIR_DOMAIN_SOUND_MODEL_ICH6: + case VIR_DOMAIN_SOUND_MODEL_ICH9: + flags = VIR_PCI_CONNECT_TYPE_PCI; + break; + } + break; + + case VIR_DOMAIN_DEVICE_VIDEO: + /* video cards aren't hot-plugged, and can be put in either a + * PCI or PCIe slot + */ + flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE; + break; + } + + /* Ignore implicit controllers on slot 0:0:1.0: + * implicit IDE controller on 0:0:1.1 (no qemu command line) + * implicit USB controller on 0:0:1.2 (-usb) + * + * If the machine does have a PCI bus, they will get reserved + * in qemuDomainAssignDevicePCISlots(). + */ + + /* These are the IDE and USB controllers in the PIIX3, hardcoded + * to bus 0 slot 1. They cannot be attached to a PCIe slot, only + * PCI. + */ + if (device->type == VIR_DOMAIN_DEVICE_CONTROLLER && addr->domain == 0 && + addr->bus == 0 && addr->slot == 1) { + virDomainControllerDefPtr cont = device->data.controller; + + if ((cont->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && cont->idx == 0 && + addr->function == 1) || + (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && cont->idx == 0 && + (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI || + cont->model == -1) && addr->function == 2)) { + /* Note the check for nbuses > 0 - if there are no PCI + * buses, we skip this check. This is a quirk required for + * some machinetypes such as s390, which pretend to have a + * PCI bus for long enough to generate the "-usb" on the + * commandline, but that don't really care if a PCI bus + * actually exists. */ + if (addrs->nbuses > 0 && + !(addrs->buses[0].flags & VIR_PCI_CONNECT_TYPE_PCI)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Bus 0 must be PCI for integrated PIIX3 " + "USB or IDE controllers")); + return -1; + } else { + return 0; + } + } + } + + entireSlot = (addr->function == 0 && + addr->multi != VIR_TRISTATE_SWITCH_ON); + + if (virDomainPCIAddressReserveAddr(addrs, addr, flags, + entireSlot, true) < 0) + goto cleanup; + + ret = 0; + cleanup: + return ret; +} + +static virDomainPCIAddressSetPtr +qemuDomainPCIAddressSetCreate(virDomainDefPtr def, + unsigned int nbuses, + bool dryRun) +{ + virDomainPCIAddressSetPtr addrs; + size_t i; + + if ((addrs = virDomainPCIAddressSetAlloc(nbuses)) == NULL) + return NULL; + + addrs->nbuses = nbuses; + addrs->dryRun = dryRun; + + /* As a safety measure, set default model='pci-root' for first pci + * controller and 'pci-bridge' for all subsequent. After setting + * those defaults, then scan the config and set the actual model + * for all addrs[idx]->bus that already have a corresponding + * controller in the config. + * + */ + if (nbuses > 0) + virDomainPCIAddressBusSetModel(&addrs->buses[0], + VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT); + for (i = 1; i < nbuses; i++) { + virDomainPCIAddressBusSetModel(&addrs->buses[i], + VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE); + } + + for (i = 0; i < def->ncontrollers; i++) { + size_t idx = def->controllers[i]->idx; + + if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI) + continue; + + if (idx >= addrs->nbuses) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Inappropriate new pci controller index %zu " + "not found in addrs"), idx); + goto error; + } + + if (virDomainPCIAddressBusSetModel(&addrs->buses[idx], + def->controllers[i]->model) < 0) + goto error; + } + + if (virDomainDeviceInfoIterate(def, qemuDomainCollectPCIAddress, addrs) < 0) + goto error; + + return addrs; + + error: + virDomainPCIAddressSetFree(addrs); + return NULL; +} + + +static int +qemuDomainValidateDevicePCISlotsPIIX3(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainPCIAddressSetPtr addrs) +{ + int ret = -1; + size_t i; + virDevicePCIAddress tmp_addr; + bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); + char *addrStr = NULL; + virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI; + + /* Verify that first IDE and USB controllers (if any) is on the PIIX3, fn 1 */ + for (i = 0; i < def->ncontrollers; i++) { + /* First IDE controller lives on the PIIX3 at slot=1, function=1 */ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && + def->controllers[i]->idx == 0) { + if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + if (def->controllers[i]->info.addr.pci.domain != 0 || + def->controllers[i]->info.addr.pci.bus != 0 || + def->controllers[i]->info.addr.pci.slot != 1 || + def->controllers[i]->info.addr.pci.function != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary IDE controller must have PCI address 0:0:1.1")); + goto cleanup; + } + } else { + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci.domain = 0; + def->controllers[i]->info.addr.pci.bus = 0; + def->controllers[i]->info.addr.pci.slot = 1; + def->controllers[i]->info.addr.pci.function = 1; + } + } else if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->idx == 0 && + (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI || + def->controllers[i]->model == -1)) { + if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + if (def->controllers[i]->info.addr.pci.domain != 0 || + def->controllers[i]->info.addr.pci.bus != 0 || + def->controllers[i]->info.addr.pci.slot != 1 || + def->controllers[i]->info.addr.pci.function != 2) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("PIIX3 USB controller must have PCI address 0:0:1.2")); + goto cleanup; + } + } else { + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci.domain = 0; + def->controllers[i]->info.addr.pci.bus = 0; + def->controllers[i]->info.addr.pci.slot = 1; + def->controllers[i]->info.addr.pci.function = 2; + } + } + } + + /* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) + * hardcoded slot=1, multifunction device + */ + if (addrs->nbuses) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 1; + if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) + goto cleanup; + } + + if (def->nvideos > 0) { + /* Because the PIIX3 integrated IDE/USB controllers are + * already at slot 1, when qemu looks for the first free slot + * to place the VGA controller (which is always the first + * device added after integrated devices), it *always* ends up + * at slot 2. + */ + virDomainVideoDefPtr primaryVideo = def->videos[0]; + if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 2; + + if (!(addrStr = virDomainPCIAddressAsString(&tmp_addr))) + goto cleanup; + if (!virDomainPCIAddressValidate(addrs, &tmp_addr, + addrStr, flags, false)) + goto cleanup; + + if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { + if (qemuDeviceVideoUsable) { + if (virDomainPCIAddressReserveNextSlot(addrs, + &primaryVideo->info, + flags) < 0) + goto cleanup; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("PCI address 0:0:2.0 is in use, " + "QEMU needs it for primary video")); + goto cleanup; + } + } else { + if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) + goto cleanup; + primaryVideo->info.addr.pci = tmp_addr; + primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + } + } else if (!qemuDeviceVideoUsable) { + if (primaryVideo->info.addr.pci.domain != 0 || + primaryVideo->info.addr.pci.bus != 0 || + primaryVideo->info.addr.pci.slot != 2 || + primaryVideo->info.addr.pci.function != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary video card must have PCI address 0:0:2.0")); + goto cleanup; + } + /* If TYPE == PCI, then qemuDomainCollectPCIAddress() function + * has already reserved the address, so we must skip */ + } + } else if (addrs->nbuses && !qemuDeviceVideoUsable) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 2; + + if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { + VIR_DEBUG("PCI address 0:0:2.0 in use, future addition of a video" + " device will not be possible without manual" + " intervention"); + } else if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) { + goto cleanup; + } + } + ret = 0; + cleanup: + VIR_FREE(addrStr); + return ret; +} + + +static int +qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainPCIAddressSetPtr addrs) +{ + int ret = -1; + size_t i; + virDevicePCIAddress tmp_addr; + bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); + char *addrStr = NULL; + virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPE_PCIE; + + for (i = 0; i < def->ncontrollers; i++) { + switch (def->controllers[i]->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_SATA: + /* Verify that the first SATA controller is at 00:1F.2 the + * q35 machine type *always* has a SATA controller at this + * address. + */ + if (def->controllers[i]->idx == 0) { + if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + if (def->controllers[i]->info.addr.pci.domain != 0 || + def->controllers[i]->info.addr.pci.bus != 0 || + def->controllers[i]->info.addr.pci.slot != 0x1F || + def->controllers[i]->info.addr.pci.function != 2) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary SATA controller must have PCI address 0:0:1f.2")); + goto cleanup; + } + } else { + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci.domain = 0; + def->controllers[i]->info.addr.pci.bus = 0; + def->controllers[i]->info.addr.pci.slot = 0x1F; + def->controllers[i]->info.addr.pci.function = 2; + } + } + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_USB: + if ((def->controllers[i]->model + == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1) && + (def->controllers[i]->info.type + == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)) { + /* Try to assign the first found USB2 controller to + * 00:1D.0 and 2nd to 00:1A.0 (because that is their + * standard location on real Q35 hardware) unless they + * are already taken, but don't insist on it. + * + * (NB: all other controllers at the same index will + * get assigned to the same slot as the UHCI1 when + * addresses are later assigned to all devices.) + */ + bool assign = false; + + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 0x1D; + if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { + assign = true; + } else { + tmp_addr.slot = 0x1A; + if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) + assign = true; + } + if (assign) { + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, + flags, false, true) < 0) + goto cleanup; + def->controllers[i]->info.type + = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci.domain = 0; + def->controllers[i]->info.addr.pci.bus = 0; + def->controllers[i]->info.addr.pci.slot = tmp_addr.slot; + def->controllers[i]->info.addr.pci.function = 0; + def->controllers[i]->info.addr.pci.multi + = VIR_TRISTATE_SWITCH_ON; + } + } + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_PCI: + if (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE && + def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + /* Try to assign this bridge to 00:1E.0 (because that + * is its standard location on real hardware) unless + * it's already taken, but don't insist on it. + */ + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 0x1E; + if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, + flags, true, false) < 0) + goto cleanup; + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci.domain = 0; + def->controllers[i]->info.addr.pci.bus = 0; + def->controllers[i]->info.addr.pci.slot = 0x1E; + def->controllers[i]->info.addr.pci.function = 0; + } + } + break; + } + } + + /* Reserve slot 0x1F function 0 (ISA bridge, not in config model) + * and function 3 (SMBus, also not (yet) in config model). As with + * the SATA controller, these devices are always present in a q35 + * machine; there is no way to not have them. + */ + if (addrs->nbuses) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 0x1F; + tmp_addr.function = 0; + tmp_addr.multi = VIR_TRISTATE_SWITCH_ON; + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, + false, false) < 0) + goto cleanup; + tmp_addr.function = 3; + tmp_addr.multi = VIR_TRISTATE_SWITCH_ABSENT; + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, + false, false) < 0) + goto cleanup; + } + + if (def->nvideos > 0) { + /* NB: unlike the pc machinetypes, on q35 machinetypes the + * integrated devices are at slot 0x1f, so when qemu looks for + * the first free lot for the first VGA, it will always be at + * slot 1 (which was used up by the integrated PIIX3 devices + * on pc machinetypes). + */ + virDomainVideoDefPtr primaryVideo = def->videos[0]; + if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 1; + + if (!(addrStr = virDomainPCIAddressAsString(&tmp_addr))) + goto cleanup; + if (!virDomainPCIAddressValidate(addrs, &tmp_addr, + addrStr, flags, false)) + goto cleanup; + + if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { + if (qemuDeviceVideoUsable) { + if (virDomainPCIAddressReserveNextSlot(addrs, + &primaryVideo->info, + flags) < 0) + goto cleanup; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("PCI address 0:0:1.0 is in use, " + "QEMU needs it for primary video")); + goto cleanup; + } + } else { + if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) + goto cleanup; + primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + primaryVideo->info.addr.pci = tmp_addr; + } + } else if (!qemuDeviceVideoUsable) { + if (primaryVideo->info.addr.pci.domain != 0 || + primaryVideo->info.addr.pci.bus != 0 || + primaryVideo->info.addr.pci.slot != 1 || + primaryVideo->info.addr.pci.function != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary video card must have PCI address 0:0:1.0")); + goto cleanup; + } + /* If TYPE == PCI, then qemuDomainCollectPCIAddress() function + * has already reserved the address, so we must skip */ + } + } else if (addrs->nbuses && !qemuDeviceVideoUsable) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 1; + + if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { + VIR_DEBUG("PCI address 0:0:1.0 in use, future addition of a video" + " device will not be possible without manual" + " intervention"); + virResetLastError(); + } else if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) { + goto cleanup; + } + } + ret = 0; + cleanup: + VIR_FREE(addrStr); + return ret; +} + + +static int +qemuDomainValidateDevicePCISlotsChipsets(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainPCIAddressSetPtr addrs) +{ + if (qemuDomainMachineIsI440FX(def) && + qemuDomainValidateDevicePCISlotsPIIX3(def, qemuCaps, addrs) < 0) { + return -1; + } + + if (qemuDomainMachineIsQ35(def) && + qemuDomainValidateDevicePCISlotsQ35(def, qemuCaps, addrs) < 0) { + return -1; + } + + return 0; +} + + +static bool +qemuDomainPCIBusFullyReserved(virDomainPCIAddressBusPtr bus) +{ + size_t i; + + for (i = bus->minSlot; i <= bus->maxSlot; i++) + if (!bus->slots[i]) + return false; + + return true; +} + + +#define IS_USB2_CONTROLLER(ctrl) \ + (((ctrl)->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) && \ + ((ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1 || \ + (ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1 || \ + (ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2 || \ + (ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3)) + +/* + * This assigns static PCI slots to all configured devices. + * The ordering here is chosen to match the ordering used + * with old QEMU < 0.12, so that if a user updates a QEMU + * host from old QEMU to QEMU >= 0.12, their guests should + * get PCI addresses in the same order as before. + * + * NB, if they previously hotplugged devices then all bets + * are off. Hotplug for old QEMU was unfixably broken wrt + * to stable PCI addressing. + * + * Order is: + * + * - Host bridge (slot 0) + * - PIIX3 ISA bridge, IDE controller, something else unknown, USB controller (slot 1) + * - Video (slot 2) + * + * - These integrated devices were already added by + * qemuValidateDevicePCISlotsChipsets invoked right before this function + * + * Incrementally assign slots from 3 onwards: + * + * - Net + * - Sound + * - SCSI controllers + * - VirtIO block + * - VirtIO balloon + * - Host device passthrough + * - Watchdog + * - pci serial devices + * + * Prior to this function being invoked, qemuDomainCollectPCIAddress() will have + * added all existing PCI addresses from the 'def' to 'addrs'. Thus this + * function must only try to reserve addresses if info.type == NONE and + * skip over info.type == PCI + */ +static int +qemuDomainAssignDevicePCISlots(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainPCIAddressSetPtr addrs) +{ + size_t i, j; + virDomainPCIConnectFlags flags; + virDevicePCIAddress tmp_addr; + + /* PCI controllers */ + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { + if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + switch (def->controllers[i]->model) { + case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT: + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT: + /* pci-root and pcie-root are implicit in the machine, + * and needs no address */ + continue; + case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: + /* pci-bridge doesn't require hot-plug + * (although it does provide hot-plug in its slots) + */ + flags = VIR_PCI_CONNECT_TYPE_PCI; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: + /* dmi-to-pci-bridge requires a non-hotplug PCIe + * slot + */ + flags = VIR_PCI_CONNECT_TYPE_PCIE; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: + /* pcie-root-port can only plug into pcie-root */ + flags = VIR_PCI_CONNECT_TYPE_PCIE_ROOT; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: + /* pcie-switch really does need a real PCIe + * port, but it doesn't need to be pcie-root + */ + flags = VIR_PCI_CONNECT_TYPE_PCIE_PORT; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: + /* pcie-switch-port can only plug into pcie-switch */ + flags = VIR_PCI_CONNECT_TYPE_PCIE_SWITCH; + break; + default: + flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI; + break; + } + if (virDomainPCIAddressReserveNextSlot(addrs, + &def->controllers[i]->info, + flags) < 0) + goto error; + } + } + + flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI; + + for (i = 0; i < def->nfss; i++) { + if (def->fss[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + /* Only support VirtIO-9p-pci so far. If that changes, + * we might need to skip devices here */ + if (virDomainPCIAddressReserveNextSlot(addrs, &def->fss[i]->info, + flags) < 0) + goto error; + } + + /* Network interfaces */ + for (i = 0; i < def->nnets; i++) { + /* type='hostdev' network devices might be USB, and are also + * in hostdevs list anyway, so handle them with other hostdevs + * instead of here. + */ + if ((def->nets[i]->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) || + (def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)) { + continue; + } + if (virDomainPCIAddressReserveNextSlot(addrs, &def->nets[i]->info, + flags) < 0) + goto error; + } + + /* Sound cards */ + for (i = 0; i < def->nsounds; i++) { + if (def->sounds[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + /* Skip ISA sound card, PCSPK and usb-audio */ + if (def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_SB16 || + def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_PCSPK || + def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_USB) + continue; + + if (virDomainPCIAddressReserveNextSlot(addrs, &def->sounds[i]->info, + flags) < 0) + goto error; + } + + /* Device controllers (SCSI, USB, but not IDE, FDC or CCID) */ + for (i = 0; i < def->ncontrollers; i++) { + /* PCI controllers have been dealt with earlier */ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) + continue; + + /* USB controller model 'none' doesn't need a PCI address */ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) + continue; + + /* FDC lives behind the ISA bridge; CCID is a usb device */ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC || + def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID) + continue; + + /* First IDE controller lives on the PIIX3 at slot=1, function=1, + dealt with earlier on*/ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && + def->controllers[i]->idx == 0) + continue; + + if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + /* USB2 needs special handling to put all companions in the same slot */ + if (IS_USB2_CONTROLLER(def->controllers[i])) { + virDevicePCIAddress addr = { 0, 0, 0, 0, false }; + bool foundAddr = false; + + memset(&tmp_addr, 0, sizeof(tmp_addr)); + for (j = 0; j < def->ncontrollers; j++) { + if (IS_USB2_CONTROLLER(def->controllers[j]) && + def->controllers[j]->idx == def->controllers[i]->idx && + def->controllers[j]->info.type + == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + addr = def->controllers[j]->info.addr.pci; + foundAddr = true; + break; + } + } + + switch (def->controllers[i]->model) { + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1: + addr.function = 7; + addr.multi = VIR_TRISTATE_SWITCH_ABSENT; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1: + addr.function = 0; + addr.multi = VIR_TRISTATE_SWITCH_ON; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2: + addr.function = 1; + addr.multi = VIR_TRISTATE_SWITCH_ABSENT; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3: + addr.function = 2; + addr.multi = VIR_TRISTATE_SWITCH_ABSENT; + break; + } + + if (!foundAddr) { + /* This is the first part of the controller, so need + * to find a free slot & then reserve a function */ + if (virDomainPCIAddressGetNextSlot(addrs, &tmp_addr, flags) < 0) + goto error; + + addr.bus = tmp_addr.bus; + addr.slot = tmp_addr.slot; + + addrs->lastaddr = addr; + addrs->lastaddr.function = 0; + addrs->lastaddr.multi = VIR_TRISTATE_SWITCH_ABSENT; + } + /* Finally we can reserve the slot+function */ + if (virDomainPCIAddressReserveAddr(addrs, &addr, flags, + false, foundAddr) < 0) + goto error; + + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci = addr; + } else { + if (virDomainPCIAddressReserveNextSlot(addrs, + &def->controllers[i]->info, + flags) < 0) + goto error; + } + } + + /* Disks (VirtIO only for now) */ + for (i = 0; i < def->ndisks; i++) { + /* Only VirtIO disks use PCI addrs */ + if (def->disks[i]->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) + continue; + + /* don't touch s390 devices */ + if (def->disks[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI || + def->disks[i]->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390 || + def->disks[i]->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) + continue; + + /* Also ignore virtio-mmio disks if our machine allows them */ + if (def->disks[i]->info.type == + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_MMIO)) + continue; + + if (def->disks[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("virtio disk cannot have an address of type '%s'"), + virDomainDeviceAddressTypeToString(def->disks[i]->info.type)); + goto error; + } + + if (virDomainPCIAddressReserveNextSlot(addrs, &def->disks[i]->info, + flags) < 0) + goto error; + } + + /* Host PCI devices */ + for (i = 0; i < def->nhostdevs; i++) { + if (def->hostdevs[i]->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + if (virDomainPCIAddressReserveNextSlot(addrs, + def->hostdevs[i]->info, + flags) < 0) + goto error; + } + + /* VirtIO balloon */ + if (def->memballoon && + def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO && + def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (virDomainPCIAddressReserveNextSlot(addrs, + &def->memballoon->info, + flags) < 0) + goto error; + } + + /* VirtIO RNG */ + for (i = 0; i < def->nrngs; i++) { + if (def->rngs[i]->model != VIR_DOMAIN_RNG_MODEL_VIRTIO || + def->rngs[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + if (virDomainPCIAddressReserveNextSlot(addrs, + &def->rngs[i]->info, flags) < 0) + goto error; + } + + /* A watchdog - check if it is a PCI device */ + if (def->watchdog && + def->watchdog->model == VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB && + def->watchdog->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (virDomainPCIAddressReserveNextSlot(addrs, &def->watchdog->info, + flags) < 0) + goto error; + } + + /* Assign a PCI slot to the primary video card if there is not an + * assigned address. */ + if (def->nvideos > 0 && + def->videos[0]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (virDomainPCIAddressReserveNextSlot(addrs, &def->videos[0]->info, + flags) < 0) + goto error; + } + + /* Further non-primary video cards which have to be qxl type */ + for (i = 1; i < def->nvideos; i++) { + if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("non-primary video device must be type of 'qxl'")); + goto error; + } + if (def->videos[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + if (virDomainPCIAddressReserveNextSlot(addrs, &def->videos[i]->info, + flags) < 0) + goto error; + } + + /* Shared Memory */ + for (i = 0; i < def->nshmems; i++) { + if (def->shmems[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + if (virDomainPCIAddressReserveNextSlot(addrs, + &def->shmems[i]->info, flags) < 0) + goto error; + } + for (i = 0; i < def->ninputs; i++) { + if (def->inputs[i]->bus != VIR_DOMAIN_INPUT_BUS_VIRTIO) + continue; + if (def->inputs[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + if (virDomainPCIAddressReserveNextSlot(addrs, + &def->inputs[i]->info, flags) < 0) + goto error; + } + for (i = 0; i < def->nparallels; i++) { + /* Nada - none are PCI based (yet) */ + } + for (i = 0; i < def->nserials; i++) { + virDomainChrDefPtr chr = def->serials[i]; + + if (chr->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI) + continue; + + if (chr->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + if (virDomainPCIAddressReserveNextSlot(addrs, &chr->info, flags) < 0) + goto error; + } + for (i = 0; i < def->nchannels; i++) { + /* Nada - none are PCI based (yet) */ + } + for (i = 0; i < def->nhubs; i++) { + /* Nada - none are PCI based (yet) */ + } + + return 0; + + error: + return -1; +} + + +static bool +qemuDomainSupportsPCI(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps) +{ + if ((def->os.arch != VIR_ARCH_ARMV7L) && (def->os.arch != VIR_ARCH_AARCH64)) + return true; + + if (STREQ(def->os.machine, "versatilepb")) + return true; + + if ((STREQ(def->os.machine, "virt") || + STRPREFIX(def->os.machine, "virt-")) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_GPEX)) + return true; + + return false; +} + + +static int +qemuDomainAssignPCIAddresses(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainObjPtr obj) +{ + int ret = -1; + virDomainPCIAddressSetPtr addrs = NULL; + qemuDomainObjPrivatePtr priv = NULL; + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { + int max_idx = -1; + int nbuses = 0; + size_t i; + int rv; + bool buses_reserved = true; + + virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPE_PCI; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { + if ((int) def->controllers[i]->idx > max_idx) + max_idx = def->controllers[i]->idx; + } + } + + nbuses = max_idx + 1; + + if (nbuses > 0 && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) { + virDomainDeviceInfo info; + + /* 1st pass to figure out how many PCI bridges we need */ + if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, true))) + goto cleanup; + + if (qemuDomainValidateDevicePCISlotsChipsets(def, qemuCaps, + addrs) < 0) + goto cleanup; + + for (i = 0; i < addrs->nbuses; i++) { + if (!qemuDomainPCIBusFullyReserved(&addrs->buses[i])) + buses_reserved = false; + } + + /* Reserve 1 extra slot for a (potential) bridge only if buses + * are not fully reserved yet + */ + if (!buses_reserved && + virDomainPCIAddressReserveNextSlot(addrs, &info, flags) < 0) + goto cleanup; + + if (qemuDomainAssignDevicePCISlots(def, qemuCaps, addrs) < 0) + goto cleanup; + + for (i = 1; i < addrs->nbuses; i++) { + virDomainPCIAddressBusPtr bus = &addrs->buses[i]; + + if ((rv = virDomainDefMaybeAddController( + def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, + i, bus->model)) < 0) + goto cleanup; + /* If we added a new bridge, we will need one more address */ + if (rv > 0 && + virDomainPCIAddressReserveNextSlot(addrs, &info, flags) < 0) + goto cleanup; + } + nbuses = addrs->nbuses; + virDomainPCIAddressSetFree(addrs); + addrs = NULL; + + } else if (max_idx > 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("PCI bridges are not supported " + "by this QEMU binary")); + goto cleanup; + } + + if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, false))) + goto cleanup; + + if (qemuDomainSupportsPCI(def, qemuCaps)) { + if (qemuDomainValidateDevicePCISlotsChipsets(def, qemuCaps, + addrs) < 0) + goto cleanup; + + if (qemuDomainAssignDevicePCISlots(def, qemuCaps, addrs) < 0) + goto cleanup; + + for (i = 0; i < def->ncontrollers; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; + int idx = cont->idx; + virDevicePCIAddressPtr addr; + virDomainPCIControllerOptsPtr options; + + if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI) + continue; + + addr = &cont->info.addr.pci; + options = &cont->opts.pciopts; + + /* set defaults for any other auto-generated config + * options for this controller that haven't been + * specified in config. + */ + switch ((virDomainControllerModelPCI)cont->model) { + case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: + if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) + options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE; + if (options->chassisNr == -1) + options->chassisNr = cont->idx; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: + if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) + options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: + if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) + options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420; + if (options->chassis == -1) + options->chassis = cont->idx; + if (options->port == -1) + options->port = (addr->slot << 3) + addr->function; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: + if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) + options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: + if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) + options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM; + if (options->chassis == -1) + options->chassis = cont->idx; + if (options->port == -1) + options->port = addr->slot; + break; + case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT: + case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT: + case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST: + break; + } + + /* check if every PCI bridge controller's ID is greater than + * the bus it is placed onto + */ + if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE && + idx <= addr->bus) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("failed to create PCI bridge " + "on bus %d: too many devices with fixed " + "addresses"), + addr->bus); + goto cleanup; + } + } + } + } + + if (obj && obj->privateData) { + priv = obj->privateData; + if (addrs) { + /* if this is the live domain object, we persist the PCI addresses*/ + virDomainPCIAddressSetFree(priv->pciaddrs); + priv->persistentAddrs = 1; + priv->pciaddrs = addrs; + addrs = NULL; + } else { + priv->persistentAddrs = 0; + } + } + + ret = 0; + + cleanup: + virDomainPCIAddressSetFree(addrs); + + return ret; +} + + +int +qemuDomainAssignAddresses(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainObjPtr obj) +{ + int rc; + + rc = qemuDomainAssignVirtioSerialAddresses(def, obj); + if (rc) + return rc; + + rc = qemuDomainAssignSpaprVIOAddresses(def, qemuCaps); + if (rc) + return rc; + + rc = qemuDomainAssignS390Addresses(def, qemuCaps, obj); + if (rc) + return rc; + + rc = qemuDomainAssignARMVirtioMMIOAddresses(def, qemuCaps); + if (rc) + return rc; + + return qemuDomainAssignPCIAddresses(def, qemuCaps, obj); +} + + +void +qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, + virDomainDeviceInfoPtr info, + const char *devstr) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + if (!devstr) + devstr = info->alias; + + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW && + qemuDomainMachineIsS390CCW(vm->def) && + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_VIRTIO_CCW) && + virDomainCCWAddressReleaseAddr(priv->ccwaddrs, info) < 0) + VIR_WARN("Unable to release CCW address on %s", + NULLSTR(devstr)); + else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && + virDomainPCIAddressReleaseSlot(priv->pciaddrs, + &info->addr.pci) < 0) + VIR_WARN("Unable to release PCI address on %s", + NULLSTR(devstr)); + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL && + virDomainVirtioSerialAddrRelease(priv->vioserialaddrs, info) < 0) + VIR_WARN("Unable to release virtio-serial address on %s", + NULLSTR(devstr)); +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 95b2e9d..88a991e 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -521,5 +521,17 @@ bool qemuDomainSupportsNetdev(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainNetDefPtr net); +int qemuDomainSetSCSIControllerModel(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + int *model); + +int qemuDomainAssignAddresses(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + virDomainObjPtr obj) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, + virDomainDeviceInfoPtr info, + const char *devstr); #endif /* __QEMU_DOMAIN_H__ */ -- 2.5.0

On Mon, Feb 15, 2016 at 02:37:22PM -0500, John Ferlan wrote:
Move the functions into qemu_domain.c - additionally move any supporting static functions.
Make qemuDomainSupportsPCI non static.
Also, move and rename the following:
qemuSetSCSIControllerModel to qemuDomainSetSCSIControllerModel qemuCollectPCIAddress to qemuDomainCollectPCIAddress qemuValidateDevicePCISlotsPIIX3 to qemuDomainValidateDevicePCISlotsPIIX3 qemuAssignDevicePCISlots to qemuDomainAssignDevicePCISlots
Most of these deal with domain addresses and qemu_domain.c is growing pretty large. I think they deserve a qemu_domain_addr.c module. (On the other hand, strictly sticking to the qemuDomainAddr prefix will make the function names too long) Jan

On 02/16/2016 08:58 AM, Ján Tomko wrote:
On Mon, Feb 15, 2016 at 02:37:22PM -0500, John Ferlan wrote:
Move the functions into qemu_domain.c - additionally move any supporting static functions.
Make qemuDomainSupportsPCI non static.
Also, move and rename the following:
qemuSetSCSIControllerModel to qemuDomainSetSCSIControllerModel qemuCollectPCIAddress to qemuDomainCollectPCIAddress qemuValidateDevicePCISlotsPIIX3 to qemuDomainValidateDevicePCISlotsPIIX3 qemuAssignDevicePCISlots to qemuDomainAssignDevicePCISlots
Most of these deal with domain addresses and qemu_domain.c is growing pretty large. I think they deserve a qemu_domain_addr.c module. (On the other hand, strictly sticking to the qemuDomainAddr prefix will make the function names too long)
Jan
So if this was a create a qemu_domain_addr.c module and move those functions in there, would that work for you? Someone else can move other virDomain*Address* functions afterwards... For some reason I have a recollection of another thread on that a while back. w/r/t the qemuDomain vs. qemuDomainAddr prefix-ing, I think qemu_hotplug.c already uses qemuDomain prefixes, so keeping virDomain (for now) should be OK. Would you want to see the adjustment? Tks - John

On Tue, Feb 16, 2016 at 09:36:58AM -0500, John Ferlan wrote:
On 02/16/2016 08:58 AM, Ján Tomko wrote:
On Mon, Feb 15, 2016 at 02:37:22PM -0500, John Ferlan wrote:
Move the functions into qemu_domain.c - additionally move any supporting static functions.
Make qemuDomainSupportsPCI non static.
Also, move and rename the following:
qemuSetSCSIControllerModel to qemuDomainSetSCSIControllerModel
ACK to the move of qemuSetSCSIControllerModel to qemu_domain.c
qemuCollectPCIAddress to qemuDomainCollectPCIAddress qemuValidateDevicePCISlotsPIIX3 to qemuDomainValidateDevicePCISlotsPIIX3 qemuAssignDevicePCISlots to qemuDomainAssignDevicePCISlots
And the rest of the functions to to qemu_domain_addr.c.
Most of these deal with domain addresses and qemu_domain.c is growing pretty large. I think they deserve a qemu_domain_addr.c module. (On the other hand, strictly sticking to the qemuDomainAddr prefix will make the function names too long)
Jan
So if this was a create a qemu_domain_addr.c module and move those functions in there, would that work for you? Someone else can move other virDomain*Address* functions afterwards... For some reason I have a recollection of another thread on that a while back.
w/r/t the qemuDomain vs. qemuDomainAddr prefix-ing, I think qemu_hotplug.c already uses qemuDomain prefixes, so keeping virDomain (for now) should be OK.
Would you want to see the adjustment?
No need for that. Jan

On 02/16/2016 09:46 AM, Ján Tomko wrote:
On Tue, Feb 16, 2016 at 09:36:58AM -0500, John Ferlan wrote:
On 02/16/2016 08:58 AM, Ján Tomko wrote:
On Mon, Feb 15, 2016 at 02:37:22PM -0500, John Ferlan wrote:
Move the functions into qemu_domain.c - additionally move any supporting static functions.
Make qemuDomainSupportsPCI non static.
Also, move and rename the following:
qemuSetSCSIControllerModel to qemuDomainSetSCSIControllerModel
ACK to the move of qemuSetSCSIControllerModel to qemu_domain.c
qemuCollectPCIAddress to qemuDomainCollectPCIAddress qemuValidateDevicePCISlotsPIIX3 to qemuDomainValidateDevicePCISlotsPIIX3 qemuAssignDevicePCISlots to qemuDomainAssignDevicePCISlots
And the rest of the functions to to qemu_domain_addr.c.
Right instead of moving that hunk of code to qemu_domain.c, I'll create qemu_domain_address.c and qemu_domain_address.h and move the hunk there instead (and of course deal with the build fallout - say nothing of the merge conflicts ;-))
Most of these deal with domain addresses and qemu_domain.c is growing pretty large. I think they deserve a qemu_domain_addr.c module. (On the other hand, strictly sticking to the qemuDomainAddr prefix will make the function names too long)
Jan
So if this was a create a qemu_domain_addr.c module and move those functions in there, would that work for you? Someone else can move other virDomain*Address* functions afterwards... For some reason I have a recollection of another thread on that a while back.
w/r/t the qemuDomain vs. qemuDomainAddr prefix-ing, I think qemu_hotplug.c already uses qemuDomain prefixes, so keeping virDomain (for now) should be OK.
Would you want to see the adjustment?
No need for that.
OK - tks - John

Move qemuDomainNetVLAN and qemuDomainDeviceAliasIndex into qemu_domain.c Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 22 ---------------------- src/qemu/qemu_command.h | 1 - src/qemu/qemu_domain.c | 25 +++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 ++++ 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f4b9dcd..a37dba6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -400,28 +400,6 @@ qemuNetworkPrepareDevices(virDomainDefPtr def) return ret; } -static int qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info, - const char *prefix) -{ - int idx; - - if (!info->alias) - return -1; - if (!STRPREFIX(info->alias, prefix)) - return -1; - - if (virStrToLong_i(info->alias + strlen(prefix), NULL, 10, &idx) < 0) - return -1; - - return idx; -} - - -int qemuDomainNetVLAN(virDomainNetDefPtr def) -{ - return qemuDomainDeviceAliasIndex(&def->info, "net"); -} - char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, virQEMUCapsPtr qemuCaps) diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index e523054..7ed0ad5 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -233,7 +233,6 @@ int qemuOpenVhostNet(virDomainDefPtr def, int qemuNetworkPrepareDevices(virDomainDefPtr def); int qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps); -int qemuDomainNetVLAN(virDomainNetDefPtr def); int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx); int qemuAssignDeviceDiskAlias(virDomainDefPtr vmdef, virDomainDiskDefPtr def, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0616574..4ae0b92 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -5987,3 +5987,28 @@ qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, VIR_WARN("Unable to release virtio-serial address on %s", NULLSTR(devstr)); } + + +int +qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info, + const char *prefix) +{ + int idx; + + if (!info->alias) + return -1; + if (!STRPREFIX(info->alias, prefix)) + return -1; + + if (virStrToLong_i(info->alias + strlen(prefix), NULL, 10, &idx) < 0) + return -1; + + return idx; +} + + +int +qemuDomainNetVLAN(virDomainNetDefPtr def) +{ + return qemuDomainDeviceAliasIndex(&def->info, "net"); +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 88a991e..85e0308 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -534,4 +534,8 @@ void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, virDomainDeviceInfoPtr info, const char *devstr); +int qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info, + const char *prefix); +int qemuDomainNetVLAN(virDomainNetDefPtr def); + #endif /* __QEMU_DOMAIN_H__ */ -- 2.5.0

On Mon, Feb 15, 2016 at 02:37:23PM -0500, John Ferlan wrote:
Move qemuDomainNetVLAN and qemuDomainDeviceAliasIndex into qemu_domain.c
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 22 ---------------------- src/qemu/qemu_command.h | 1 - src/qemu/qemu_domain.c | 25 +++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 ++++ 4 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f4b9dcd..a37dba6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -400,28 +400,6 @@ qemuNetworkPrepareDevices(virDomainDefPtr def) return ret; }
-static int qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info, - const char *prefix)
This one would fit better together with the other Alias functions. Jan

On 02/16/2016 09:08 AM, Ján Tomko wrote:
On Mon, Feb 15, 2016 at 02:37:23PM -0500, John Ferlan wrote:
Move qemuDomainNetVLAN and qemuDomainDeviceAliasIndex into qemu_domain.c
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 22 ---------------------- src/qemu/qemu_command.h | 1 - src/qemu/qemu_domain.c | 25 +++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 ++++ 4 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f4b9dcd..a37dba6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -400,28 +400,6 @@ qemuNetworkPrepareDevices(virDomainDefPtr def) return ret; }
-static int qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info, - const char *prefix)
This one would fit better together with the other Alias functions.
OK - I flipped a coin and it landed on qemu_domain.c ;-)... Also the existing qemu_assign_alias.c were more assigning not parsing and fetching a "piece" of the alias. John

Move function to qemu_interface.c and rename to qemuInterfaceOpenVhostNet Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 97 +---------------------------------------------- src/qemu/qemu_command.h | 6 --- src/qemu/qemu_hotplug.c | 9 +++-- src/qemu/qemu_interface.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_interface.h | 7 ++++ 5 files changed, 112 insertions(+), 104 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index a37dba6..60dac2f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -257,100 +257,6 @@ qemuBuildObjectCommandlineFromJSON(const char *type, } -/** - * qemuOpenVhostNet: - * @def: domain definition - * @net: network definition - * @qemuCaps: qemu binary capabilities - * @vhostfd: array of opened vhost-net device - * @vhostfdSize: number of file descriptors in @vhostfd array - * - * Open vhost-net, multiple times - if requested. - * In case, no vhost-net is needed, @vhostfdSize is set to 0 - * and 0 is returned. - * - * Returns: 0 on success - * -1 on failure - */ -int -qemuOpenVhostNet(virDomainDefPtr def, - virDomainNetDefPtr net, - virQEMUCapsPtr qemuCaps, - int *vhostfd, - size_t *vhostfdSize) -{ - size_t i; - const char *vhostnet_path = net->backend.vhost; - - if (!vhostnet_path) - vhostnet_path = "/dev/vhost-net"; - - /* If running a plain QEMU guest, or - * if the config says explicitly to not use vhost, return now*/ - if (def->virtType != VIR_DOMAIN_VIRT_KVM || - net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_QEMU) { - *vhostfdSize = 0; - return 0; - } - - /* If qemu doesn't support vhost-net mode (including the -netdev command - * option), don't try to open the device. - */ - if (!(virQEMUCapsGet(qemuCaps, QEMU_CAPS_VHOST_NET) && - qemuDomainSupportsNetdev(def, qemuCaps, net))) { - if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("vhost-net is not supported with " - "this QEMU binary")); - return -1; - } - *vhostfdSize = 0; - return 0; - } - - /* If the nic model isn't virtio, don't try to open. */ - if (!(net->model && STREQ(net->model, "virtio"))) { - if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("vhost-net is only supported for " - "virtio network interfaces")); - return -1; - } - *vhostfdSize = 0; - return 0; - } - - for (i = 0; i < *vhostfdSize; i++) { - vhostfd[i] = open(vhostnet_path, O_RDWR); - - /* If the config says explicitly to use vhost and we couldn't open it, - * report an error. - */ - if (vhostfd[i] < 0) { - virDomainAuditNetDevice(def, net, vhostnet_path, false); - if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("vhost-net was requested for an interface, " - "but is unavailable")); - goto error; - } - VIR_WARN("Unable to open vhost-net. Opened so far %zu, requested %zu", - i, *vhostfdSize); - *vhostfdSize = i; - break; - } - } - virDomainAuditNetDevice(def, net, vhostnet_path, *vhostfdSize); - return 0; - - error: - while (i--) - VIR_FORCE_CLOSE(vhostfd[i]); - - return -1; -} - - int qemuNetworkPrepareDevices(virDomainDefPtr def) { @@ -6672,7 +6578,8 @@ qemuBuildInterfaceCommandLine(virCommandPtr cmd, memset(vhostfd, -1, vhostfdSize * sizeof(vhostfd[0])); - if (qemuOpenVhostNet(def, net, qemuCaps, vhostfd, &vhostfdSize) < 0) + if (qemuInterfaceOpenVhostNet(def, net, qemuCaps, + vhostfd, &vhostfdSize) < 0) goto cleanup; } diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 7ed0ad5..61a0212 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -224,12 +224,6 @@ char *qemuBuildRedirdevDevStr(virDomainDefPtr def, virDomainRedirdevDefPtr dev, virQEMUCapsPtr qemuCaps); -int qemuOpenVhostNet(virDomainDefPtr def, - virDomainNetDefPtr net, - virQEMUCapsPtr qemuCaps, - int *vhostfd, - size_t *vhostfdSize); - int qemuNetworkPrepareDevices(virDomainDefPtr def); int qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index e635fab..71cfc79 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -926,7 +926,8 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, tapfd, &tapfdSize) < 0) goto cleanup; iface_connected = true; - if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, vhostfd, &vhostfdSize) < 0) + if (qemuInterfaceOpenVhostNet(vm->def, net, priv->qemuCaps, + vhostfd, &vhostfdSize) < 0) goto cleanup; } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { tapfdSize = vhostfdSize = net->driver.virtio.queues; @@ -943,14 +944,16 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, VIR_NETDEV_VPORT_PROFILE_OP_CREATE) < 0) goto cleanup; iface_connected = true; - if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, vhostfd, &vhostfdSize) < 0) + if (qemuInterfaceOpenVhostNet(vm->def, net, priv->qemuCaps, + vhostfd, &vhostfdSize) < 0) goto cleanup; } else if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET) { vhostfdSize = 1; if (VIR_ALLOC(vhostfd) < 0) goto cleanup; *vhostfd = -1; - if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, vhostfd, &vhostfdSize) < 0) + if (qemuInterfaceOpenVhostNet(vm->def, net, priv->qemuCaps, + vhostfd, &vhostfdSize) < 0) goto cleanup; } diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index 2584236..79507af 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -38,6 +38,9 @@ #include "virnetdevbridge.h" #include "virnetdevvportprofile.h" +#include <sys/stat.h> +#include <fcntl.h> + #define VIR_FROM_THIS VIR_FROM_QEMU VIR_LOG_INIT("qemu.qemu_interface"); @@ -500,3 +503,97 @@ qemuInterfaceNetworkConnect(virDomainDefPtr def, return ret; } + + +/** + * qemuInterfaceOpenVhostNet: + * @def: domain definition + * @net: network definition + * @qemuCaps: qemu binary capabilities + * @vhostfd: array of opened vhost-net device + * @vhostfdSize: number of file descriptors in @vhostfd array + * + * Open vhost-net, multiple times - if requested. + * In case, no vhost-net is needed, @vhostfdSize is set to 0 + * and 0 is returned. + * + * Returns: 0 on success + * -1 on failure + */ +int +qemuInterfaceOpenVhostNet(virDomainDefPtr def, + virDomainNetDefPtr net, + virQEMUCapsPtr qemuCaps, + int *vhostfd, + size_t *vhostfdSize) +{ + size_t i; + const char *vhostnet_path = net->backend.vhost; + + if (!vhostnet_path) + vhostnet_path = "/dev/vhost-net"; + + /* If running a plain QEMU guest, or + * if the config says explicitly to not use vhost, return now*/ + if (def->virtType != VIR_DOMAIN_VIRT_KVM || + net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_QEMU) { + *vhostfdSize = 0; + return 0; + } + + /* If qemu doesn't support vhost-net mode (including the -netdev command + * option), don't try to open the device. + */ + if (!(virQEMUCapsGet(qemuCaps, QEMU_CAPS_VHOST_NET) && + qemuDomainSupportsNetdev(def, qemuCaps, net))) { + if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("vhost-net is not supported with " + "this QEMU binary")); + return -1; + } + *vhostfdSize = 0; + return 0; + } + + /* If the nic model isn't virtio, don't try to open. */ + if (!(net->model && STREQ(net->model, "virtio"))) { + if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("vhost-net is only supported for " + "virtio network interfaces")); + return -1; + } + *vhostfdSize = 0; + return 0; + } + + for (i = 0; i < *vhostfdSize; i++) { + vhostfd[i] = open(vhostnet_path, O_RDWR); + + /* If the config says explicitly to use vhost and we couldn't open it, + * report an error. + */ + if (vhostfd[i] < 0) { + virDomainAuditNetDevice(def, net, vhostnet_path, false); + if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("vhost-net was requested for an interface, " + "but is unavailable")); + goto error; + } + VIR_WARN("Unable to open vhost-net. Opened so far %zu, requested %zu", + i, *vhostfdSize); + *vhostfdSize = i; + break; + } + } + virDomainAuditNetDevice(def, net, vhostnet_path, *vhostfdSize); + return 0; + + error: + while (i--) + VIR_FORCE_CLOSE(vhostfd[i]); + + return -1; +} diff --git a/src/qemu/qemu_interface.h b/src/qemu/qemu_interface.h index 9cc7331..8207e9f 100644 --- a/src/qemu/qemu_interface.h +++ b/src/qemu/qemu_interface.h @@ -27,6 +27,7 @@ # include "domain_conf.h" # include "qemu_conf.h" +# include "qemu_domain.h" int qemuInterfaceStartDevice(virDomainNetDefPtr net); int qemuInterfaceStartDevices(virDomainDefPtr def); @@ -46,4 +47,10 @@ int qemuInterfaceNetworkConnect(virDomainDefPtr def, int *tapfd, size_t *tapfdSize) ATTRIBUTE_NONNULL(2); + +int qemuInterfaceOpenVhostNet(virDomainDefPtr def, + virDomainNetDefPtr net, + virQEMUCapsPtr qemuCaps, + int *vhostfd, + size_t *vhostfdSize); #endif /* __QEMU_INTERFACE_H__ */ -- 2.5.0

On Mon, Feb 15, 2016 at 02:37:24PM -0500, John Ferlan wrote:
Move function to qemu_interface.c and rename to qemuInterfaceOpenVhostNet
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 97 +---------------------------------------------- src/qemu/qemu_command.h | 6 --- src/qemu/qemu_hotplug.c | 9 +++-- src/qemu/qemu_interface.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_interface.h | 7 ++++ 5 files changed, 112 insertions(+), 104 deletions(-)
ACK Jan

Move function to qemu_process.c, rename to qemuSetupNetworkPrepareDevices and make it static. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 50 -------------------------------------------- src/qemu/qemu_process.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 60dac2f..edc5379 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -257,56 +257,6 @@ qemuBuildObjectCommandlineFromJSON(const char *type, } -int -qemuNetworkPrepareDevices(virDomainDefPtr def) -{ - int ret = -1; - size_t i; - - for (i = 0; i < def->nnets; i++) { - virDomainNetDefPtr net = def->nets[i]; - int actualType; - - /* If appropriate, grab a physical device from the configured - * network's pool of devices, or resolve bridge device name - * to the one defined in the network definition. - */ - if (networkAllocateActualDevice(def, net) < 0) - goto cleanup; - - actualType = virDomainNetGetActualType(net); - if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV && - net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - /* Each type='hostdev' network device must also have a - * corresponding entry in the hostdevs array. For netdevs - * that are hardcoded as type='hostdev', this is already - * done by the parser, but for those allocated from a - * network / determined at runtime, we need to do it - * separately. - */ - virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net); - virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci; - - if (virDomainHostdevFind(def, hostdev, NULL) >= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("PCI device %04x:%02x:%02x.%x " - "allocated from network %s is already " - "in use by domain %s"), - pcisrc->addr.domain, pcisrc->addr.bus, - pcisrc->addr.slot, pcisrc->addr.function, - net->data.network.name, def->name); - goto cleanup; - } - if (virDomainHostdevInsert(def, hostdev) < 0) - goto cleanup; - } - } - ret = 0; - cleanup: - return ret; -} - - char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, virQEMUCapsPtr qemuCaps) { diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 23238df..c4a15ac 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4401,6 +4401,59 @@ qemuProcessInit(virQEMUDriverPtr driver, /** + * qemuProcessNetworkPrepareDevices + */ +static int +qemuProcessNetworkPrepareDevices(virDomainDefPtr def) +{ + int ret = -1; + size_t i; + + for (i = 0; i < def->nnets; i++) { + virDomainNetDefPtr net = def->nets[i]; + int actualType; + + /* If appropriate, grab a physical device from the configured + * network's pool of devices, or resolve bridge device name + * to the one defined in the network definition. + */ + if (networkAllocateActualDevice(def, net) < 0) + goto cleanup; + + actualType = virDomainNetGetActualType(net); + if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV && + net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + /* Each type='hostdev' network device must also have a + * corresponding entry in the hostdevs array. For netdevs + * that are hardcoded as type='hostdev', this is already + * done by the parser, but for those allocated from a + * network / determined at runtime, we need to do it + * separately. + */ + virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net); + virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci; + + if (virDomainHostdevFind(def, hostdev, NULL) >= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("PCI device %04x:%02x:%02x.%x " + "allocated from network %s is already " + "in use by domain %s"), + pcisrc->addr.domain, pcisrc->addr.bus, + pcisrc->addr.slot, pcisrc->addr.function, + net->data.network.name, def->name); + goto cleanup; + } + if (virDomainHostdevInsert(def, hostdev) < 0) + goto cleanup; + } + } + ret = 0; + cleanup: + return ret; +} + + +/** * qemuProcessSetupVcpu: * @vm: domain object * @vcpuid: id of VCPU to set defaults @@ -4726,7 +4779,7 @@ qemuProcessLaunch(virConnectPtr conn, * will need to be setup. */ VIR_DEBUG("Preparing network devices"); - if (qemuNetworkPrepareDevices(vm->def) < 0) + if (qemuProcessNetworkPrepareDevices(vm->def) < 0) goto cleanup; /* Must be run before security labelling */ -- 2.5.0

d/-command/ On Mon, Feb 15, 2016 at 02:37:25PM -0500, John Ferlan wrote:
Move function to qemu_process.c, rename to qemuSetupNetworkPrepareDevices
s/Setup/Process/
and make it static.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 50 -------------------------------------------- src/qemu/qemu_process.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 51 deletions(-)
ACK Jan

On 02/16/2016 09:16 AM, Ján Tomko wrote:
d/-command/
On Mon, Feb 15, 2016 at 02:37:25PM -0500, John Ferlan wrote:
Move function to qemu_process.c, rename to qemuSetupNetworkPrepareDevices s/Setup/Process/
and make it static.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 50 -------------------------------------------- src/qemu/qemu_process.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 51 deletions(-)
ACK
Actually I would have put this one in qemu_interface.c

Create a new module qemu_assign_alias.c to handle the qemuAssign*Alias* APIs Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_assign_alias.c | 468 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_assign_alias.h | 60 ++++++ src/qemu/qemu_command.c | 427 +-------------------------------------- src/qemu/qemu_command.h | 16 -- src/qemu/qemu_driver.c | 1 + src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_process.c | 1 + tests/qemuhotplugtest.c | 1 + tests/qemuxml2argvtest.c | 1 + 11 files changed, 536 insertions(+), 442 deletions(-) create mode 100644 src/qemu/qemu_assign_alias.c create mode 100644 src/qemu/qemu_assign_alias.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0ca9757..93b09b6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -120,6 +120,7 @@ src/vz/vz_utils.c src/vz/vz_utils.h src/phyp/phyp_driver.c src/qemu/qemu_agent.c +src/qemu/qemu_assign_alias.c src/qemu/qemu_capabilities.c src/qemu/qemu_cgroup.c src/qemu/qemu_command.c diff --git a/src/Makefile.am b/src/Makefile.am index f857e59..91a66c8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -791,6 +791,7 @@ VBOX_DRIVER_EXTRA_DIST = \ QEMU_DRIVER_SOURCES = \ qemu/qemu_agent.c qemu/qemu_agent.h \ + qemu/qemu_assign_alias.c qemu/qemu_assign_alias.h \ qemu/qemu_blockjob.c qemu/qemu_blockjob.h \ qemu/qemu_capabilities.c qemu/qemu_capabilities.h \ qemu/qemu_command.c qemu/qemu_command.h \ diff --git a/src/qemu/qemu_assign_alias.c b/src/qemu/qemu_assign_alias.c new file mode 100644 index 0000000..8aee2a5 --- /dev/null +++ b/src/qemu/qemu_assign_alias.c @@ -0,0 +1,468 @@ +/* + * qemu_assign_alias.c: QEMU assign aliases + * + * Copyright (C) 2006-2016 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "qemu_assign_alias.h" +#include "viralloc.h" +#include "virlog.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.qemu_assign_alias"); + +static ssize_t +qemuGetNextChrDevIndex(virDomainDefPtr def, + virDomainChrDefPtr chr, + const char *prefix) +{ + const virDomainChrDef **arrPtr; + size_t cnt; + size_t i; + ssize_t idx = 0; + const char *prefix2 = NULL; + + if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE) + prefix2 = "serial"; + + virDomainChrGetDomainPtrs(def, chr->deviceType, &arrPtr, &cnt); + + for (i = 0; i < cnt; i++) { + ssize_t thisidx; + if (((thisidx = qemuDomainDeviceAliasIndex(&arrPtr[i]->info, prefix)) < 0) && + (prefix2 && + (thisidx = qemuDomainDeviceAliasIndex(&arrPtr[i]->info, prefix2)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine device index for character device")); + return -1; + } + if (thisidx >= idx) + idx = thisidx + 1; + } + + return idx; +} + + +int +qemuAssignDeviceChrAlias(virDomainDefPtr def, + virDomainChrDefPtr chr, + ssize_t idx) +{ + const char *prefix = NULL; + + switch ((virDomainChrDeviceType) chr->deviceType) { + case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: + prefix = "parallel"; + break; + + case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: + prefix = "serial"; + break; + + case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: + prefix = "console"; + break; + + case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: + prefix = "channel"; + break; + + case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: + return -1; + } + + if (idx == -1 && (idx = qemuGetNextChrDevIndex(def, chr, prefix)) < 0) + return -1; + + return virAsprintf(&chr->info.alias, "%s%zd", prefix, idx); +} + + +int +qemuAssignDeviceControllerAlias(virDomainDefPtr domainDef, + virQEMUCapsPtr qemuCaps, + virDomainControllerDefPtr controller) +{ + const char *prefix = virDomainControllerTypeToString(controller->type); + + if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { + if (!virQEMUCapsHasPCIMultiBus(qemuCaps, domainDef)) { + /* qemus that don't support multiple PCI buses have + * hardcoded the name of their single PCI controller as + * "pci". + */ + return VIR_STRDUP(controller->info.alias, "pci"); + } else if (controller->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) { + /* The pcie-root controller on Q35 machinetypes uses a + * different naming convention ("pcie.0"), because it is + * hardcoded that way in qemu. + */ + return virAsprintf(&controller->info.alias, "pcie.%d", controller->idx); + } + /* All other PCI controllers use the consistent "pci.%u" + * (including the hardcoded pci-root controller on + * multibus-capable qemus). + */ + return virAsprintf(&controller->info.alias, "pci.%d", controller->idx); + } else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE) { + /* for any machine based on e.g. I440FX or G3Beige, the + * first (and currently only) IDE controller is an integrated + * controller hardcoded with id "ide" + */ + if (qemuDomainMachineHasBuiltinIDE(domainDef) && + controller->idx == 0) + return VIR_STRDUP(controller->info.alias, "ide"); + } else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) { + /* for any Q35 machine, the first SATA controller is the + * integrated one, and it too is hardcoded with id "ide" + */ + if (qemuDomainMachineIsQ35(domainDef) && controller->idx == 0) + return VIR_STRDUP(controller->info.alias, "ide"); + } else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) { + /* first USB device is "usb", others are normal "usb%d" */ + if (controller->idx == 0) + return VIR_STRDUP(controller->info.alias, "usb"); + } + /* all other controllers use the default ${type}${index} naming + * scheme for alias/id. + */ + return virAsprintf(&controller->info.alias, "%s%d", prefix, controller->idx); +} + + +/* Names used before -drive supported the id= option */ +static int +qemuAssignDeviceDiskAliasFixed(virDomainDiskDefPtr disk) +{ + int busid, devid; + int ret; + char *dev_name; + + if (virDiskNameToBusDeviceIndex(disk, &busid, &devid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot convert disk '%s' to bus/device index"), + disk->dst); + return -1; + } + + switch (disk->bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) + ret = virAsprintf(&dev_name, "ide%d-hd%d", busid, devid); + else + ret = virAsprintf(&dev_name, "ide%d-cd%d", busid, devid); + break; + case VIR_DOMAIN_DISK_BUS_SCSI: + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) + ret = virAsprintf(&dev_name, "scsi%d-hd%d", busid, devid); + else + ret = virAsprintf(&dev_name, "scsi%d-cd%d", busid, devid); + break; + case VIR_DOMAIN_DISK_BUS_FDC: + ret = virAsprintf(&dev_name, "floppy%d", devid); + break; + case VIR_DOMAIN_DISK_BUS_VIRTIO: + ret = virAsprintf(&dev_name, "virtio%d", devid); + break; + case VIR_DOMAIN_DISK_BUS_XEN: + ret = virAsprintf(&dev_name, "xenblk%d", devid); + break; + case VIR_DOMAIN_DISK_BUS_SD: + ret = virAsprintf(&dev_name, "sd%d", devid); + break; + case VIR_DOMAIN_DISK_BUS_USB: + ret = virAsprintf(&dev_name, "usb%d", devid); + break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported disk name mapping for bus '%s'"), + virDomainDiskBusTypeToString(disk->bus)); + return -1; + } + + if (ret == -1) + return -1; + + disk->info.alias = dev_name; + + return 0; +} + + +/* Our custom -drive naming scheme used with id= */ +static int +qemuAssignDeviceDiskAliasCustom(virDomainDefPtr def, + virDomainDiskDefPtr disk, + virQEMUCapsPtr qemuCaps) +{ + const char *prefix = virDomainDiskBusTypeToString(disk->bus); + int controllerModel = -1; + + if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) { + controllerModel = + virDomainDeviceFindControllerModel(def, &disk->info, + VIR_DOMAIN_CONTROLLER_TYPE_SCSI); + + if ((qemuDomainSetSCSIControllerModel(def, qemuCaps, + &controllerModel)) < 0) + return -1; + } + + if (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI || + controllerModel == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) { + if (virAsprintf(&disk->info.alias, "%s%d-%d-%d", prefix, + disk->info.addr.drive.controller, + disk->info.addr.drive.bus, + disk->info.addr.drive.unit) < 0) + return -1; + } else { + if (virAsprintf(&disk->info.alias, "%s%d-%d-%d-%d", prefix, + disk->info.addr.drive.controller, + disk->info.addr.drive.bus, + disk->info.addr.drive.target, + disk->info.addr.drive.unit) < 0) + return -1; + } + } else { + int idx = virDiskNameToIndex(disk->dst); + if (virAsprintf(&disk->info.alias, "%s-disk%d", prefix, idx) < 0) + return -1; + } + + return 0; +} + + +int +qemuAssignDeviceDiskAlias(virDomainDefPtr vmdef, + virDomainDiskDefPtr def, + virQEMUCapsPtr qemuCaps) +{ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) + return qemuAssignDeviceDiskAliasCustom(vmdef, def, qemuCaps); + else + return qemuAssignDeviceDiskAliasFixed(def); +} + + +int +qemuAssignDeviceHostdevAlias(virDomainDefPtr def, + virDomainHostdevDefPtr hostdev, + int idx) +{ + if (idx == -1) { + size_t i; + idx = 0; + for (i = 0; i < def->nhostdevs; i++) { + int thisidx; + if ((thisidx = qemuDomainDeviceAliasIndex(def->hostdevs[i]->info, "hostdev")) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine device index for hostdev device")); + return -1; + } + if (thisidx >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx) < 0) + return -1; + + return 0; +} + + +int +qemuAssignDeviceNetAlias(virDomainDefPtr def, + virDomainNetDefPtr net, + int idx) +{ + if (idx == -1) { + size_t i; + idx = 0; + for (i = 0; i < def->nnets; i++) { + int thisidx; + + if (virDomainNetGetActualType(def->nets[i]) + == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + /* type='hostdev' interfaces have a hostdev%d alias */ + continue; + } + if ((thisidx = qemuDomainDeviceAliasIndex(&def->nets[i]->info, "net")) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine device index for network device")); + return -1; + } + if (thisidx >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&net->info.alias, "net%d", idx) < 0) + return -1; + return 0; +} + + +int +qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, + virDomainRedirdevDefPtr redirdev, + int idx) +{ + if (idx == -1) { + size_t i; + idx = 0; + for (i = 0; i < def->nredirdevs; i++) { + int thisidx; + if ((thisidx = qemuDomainDeviceAliasIndex(&def->redirdevs[i]->info, "redir")) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine device index for redirected device")); + return -1; + } + if (thisidx >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&redirdev->info.alias, "redir%d", idx) < 0) + return -1; + return 0; +} + + +int +qemuAssignDeviceRNGAlias(virDomainRNGDefPtr rng, + size_t idx) +{ + if (virAsprintf(&rng->info.alias, "rng%zu", idx) < 0) + return -1; + + return 0; +} + + +int +qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) +{ + size_t i; + + for (i = 0; i < def->ndisks; i++) { + if (qemuAssignDeviceDiskAlias(def, def->disks[i], qemuCaps) < 0) + return -1; + } + for (i = 0; i < def->nnets; i++) { + /* type='hostdev' interfaces are also on the hostdevs list, + * and will have their alias assigned with other hostdevs. + */ + if (virDomainNetGetActualType(def->nets[i]) + != VIR_DOMAIN_NET_TYPE_HOSTDEV && + qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0) { + return -1; + } + } + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) + return 0; + + for (i = 0; i < def->nfss; i++) { + if (virAsprintf(&def->fss[i]->info.alias, "fs%zu", i) < 0) + return -1; + } + for (i = 0; i < def->nsounds; i++) { + if (virAsprintf(&def->sounds[i]->info.alias, "sound%zu", i) < 0) + return -1; + } + for (i = 0; i < def->nhostdevs; i++) { + if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0) + return -1; + } + for (i = 0; i < def->nredirdevs; i++) { + if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0) + return -1; + } + for (i = 0; i < def->nvideos; i++) { + if (virAsprintf(&def->videos[i]->info.alias, "video%zu", i) < 0) + return -1; + } + for (i = 0; i < def->ncontrollers; i++) { + if (qemuAssignDeviceControllerAlias(def, qemuCaps, def->controllers[i]) < 0) + return -1; + } + for (i = 0; i < def->ninputs; i++) { + if (virAsprintf(&def->inputs[i]->info.alias, "input%zu", i) < 0) + return -1; + } + for (i = 0; i < def->nparallels; i++) { + if (qemuAssignDeviceChrAlias(def, def->parallels[i], i) < 0) + return -1; + } + for (i = 0; i < def->nserials; i++) { + if (qemuAssignDeviceChrAlias(def, def->serials[i], i) < 0) + return -1; + } + for (i = 0; i < def->nchannels; i++) { + if (qemuAssignDeviceChrAlias(def, def->channels[i], i) < 0) + return -1; + } + for (i = 0; i < def->nconsoles; i++) { + if (qemuAssignDeviceChrAlias(def, def->consoles[i], i) < 0) + return -1; + } + for (i = 0; i < def->nhubs; i++) { + if (virAsprintf(&def->hubs[i]->info.alias, "hub%zu", i) < 0) + return -1; + } + for (i = 0; i < def->nshmems; i++) { + if (virAsprintf(&def->shmems[i]->info.alias, "shmem%zu", i) < 0) + return -1; + } + for (i = 0; i < def->nsmartcards; i++) { + if (virAsprintf(&def->smartcards[i]->info.alias, "smartcard%zu", i) < 0) + return -1; + } + if (def->watchdog) { + if (virAsprintf(&def->watchdog->info.alias, "watchdog%d", 0) < 0) + return -1; + } + if (def->memballoon) { + if (virAsprintf(&def->memballoon->info.alias, "balloon%d", 0) < 0) + return -1; + } + for (i = 0; i < def->nrngs; i++) { + if (qemuAssignDeviceRNGAlias(def->rngs[i], i) < 0) + return -1; + } + if (def->tpm) { + if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0) + return -1; + } + for (i = 0; i < def->nmems; i++) { + if (virAsprintf(&def->mems[i]->info.alias, "dimm%zu", i) < 0) + return -1; + } + + return 0; +} diff --git a/src/qemu/qemu_assign_alias.h b/src/qemu/qemu_assign_alias.h new file mode 100644 index 0000000..7fc418c --- /dev/null +++ b/src/qemu/qemu_assign_alias.h @@ -0,0 +1,60 @@ +/* + * qemu_assign_alias.h: QEMU assign aliases + * + * Copyright (C) 2006-2016 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_ASSIGN_ALIAS_H__ +# define __QEMU_ASSIGN_ALIAS_H__ + +# include "domain_conf.h" + +# include "qemu_capabilities.h" +# include "qemu_domain.h" + +int qemuAssignDeviceChrAlias(virDomainDefPtr def, + virDomainChrDefPtr chr, + ssize_t idx); + +int qemuAssignDeviceControllerAlias(virDomainDefPtr domainDef, + virQEMUCapsPtr qemuCaps, + virDomainControllerDefPtr controller); + +int qemuAssignDeviceDiskAlias(virDomainDefPtr vmdef, + virDomainDiskDefPtr def, + virQEMUCapsPtr qemuCaps); + +int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, + virDomainHostdevDefPtr hostdev, + int idx); + +int qemuAssignDeviceNetAlias(virDomainDefPtr def, + virDomainNetDefPtr net, + int idx); + +int qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, + virDomainRedirdevDefPtr redirdev, + int idx); + +int qemuAssignDeviceRNGAlias(virDomainRNGDefPtr rng, + size_t idx); + +int qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps); +#endif /* __QEMU_ASSIGN_ALIAS_H__*/ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index edc5379..22d25df 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -27,6 +27,7 @@ #include "qemu_hostdev.h" #include "qemu_capabilities.h" #include "qemu_interface.h" +#include "qemu_assign_alias.h" #include "cpu/cpu.h" #include "dirname.h" #include "viralloc.h" @@ -272,432 +273,6 @@ char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, } -/* Names used before -drive supported the id= option */ -static int qemuAssignDeviceDiskAliasFixed(virDomainDiskDefPtr disk) -{ - int busid, devid; - int ret; - char *dev_name; - - if (virDiskNameToBusDeviceIndex(disk, &busid, &devid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot convert disk '%s' to bus/device index"), - disk->dst); - return -1; - } - - switch (disk->bus) { - case VIR_DOMAIN_DISK_BUS_IDE: - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) - ret = virAsprintf(&dev_name, "ide%d-hd%d", busid, devid); - else - ret = virAsprintf(&dev_name, "ide%d-cd%d", busid, devid); - break; - case VIR_DOMAIN_DISK_BUS_SCSI: - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) - ret = virAsprintf(&dev_name, "scsi%d-hd%d", busid, devid); - else - ret = virAsprintf(&dev_name, "scsi%d-cd%d", busid, devid); - break; - case VIR_DOMAIN_DISK_BUS_FDC: - ret = virAsprintf(&dev_name, "floppy%d", devid); - break; - case VIR_DOMAIN_DISK_BUS_VIRTIO: - ret = virAsprintf(&dev_name, "virtio%d", devid); - break; - case VIR_DOMAIN_DISK_BUS_XEN: - ret = virAsprintf(&dev_name, "xenblk%d", devid); - break; - case VIR_DOMAIN_DISK_BUS_SD: - ret = virAsprintf(&dev_name, "sd%d", devid); - break; - case VIR_DOMAIN_DISK_BUS_USB: - ret = virAsprintf(&dev_name, "usb%d", devid); - break; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported disk name mapping for bus '%s'"), - virDomainDiskBusTypeToString(disk->bus)); - return -1; - } - - if (ret == -1) - return -1; - - disk->info.alias = dev_name; - - return 0; -} - -/* Our custom -drive naming scheme used with id= */ -static int -qemuAssignDeviceDiskAliasCustom(virDomainDefPtr def, - virDomainDiskDefPtr disk, - virQEMUCapsPtr qemuCaps) -{ - const char *prefix = virDomainDiskBusTypeToString(disk->bus); - int controllerModel = -1; - - if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) { - controllerModel = - virDomainDeviceFindControllerModel(def, &disk->info, - VIR_DOMAIN_CONTROLLER_TYPE_SCSI); - - if ((qemuDomainSetSCSIControllerModel(def, qemuCaps, - &controllerModel)) < 0) - return -1; - } - - if (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI || - controllerModel == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) { - if (virAsprintf(&disk->info.alias, "%s%d-%d-%d", prefix, - disk->info.addr.drive.controller, - disk->info.addr.drive.bus, - disk->info.addr.drive.unit) < 0) - return -1; - } else { - if (virAsprintf(&disk->info.alias, "%s%d-%d-%d-%d", prefix, - disk->info.addr.drive.controller, - disk->info.addr.drive.bus, - disk->info.addr.drive.target, - disk->info.addr.drive.unit) < 0) - return -1; - } - } else { - int idx = virDiskNameToIndex(disk->dst); - if (virAsprintf(&disk->info.alias, "%s-disk%d", prefix, idx) < 0) - return -1; - } - - return 0; -} - - -int -qemuAssignDeviceDiskAlias(virDomainDefPtr vmdef, - virDomainDiskDefPtr def, - virQEMUCapsPtr qemuCaps) -{ - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) - return qemuAssignDeviceDiskAliasCustom(vmdef, def, qemuCaps); - else - return qemuAssignDeviceDiskAliasFixed(def); -} - - -int -qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx) -{ - if (idx == -1) { - size_t i; - idx = 0; - for (i = 0; i < def->nnets; i++) { - int thisidx; - - if (virDomainNetGetActualType(def->nets[i]) - == VIR_DOMAIN_NET_TYPE_HOSTDEV) { - /* type='hostdev' interfaces have a hostdev%d alias */ - continue; - } - if ((thisidx = qemuDomainDeviceAliasIndex(&def->nets[i]->info, "net")) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to determine device index for network device")); - return -1; - } - if (thisidx >= idx) - idx = thisidx + 1; - } - } - - if (virAsprintf(&net->info.alias, "net%d", idx) < 0) - return -1; - return 0; -} - - -int -qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx) -{ - if (idx == -1) { - size_t i; - idx = 0; - for (i = 0; i < def->nhostdevs; i++) { - int thisidx; - if ((thisidx = qemuDomainDeviceAliasIndex(def->hostdevs[i]->info, "hostdev")) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to determine device index for hostdev device")); - return -1; - } - if (thisidx >= idx) - idx = thisidx + 1; - } - } - - if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx) < 0) - return -1; - - return 0; -} - - -int -qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx) -{ - if (idx == -1) { - size_t i; - idx = 0; - for (i = 0; i < def->nredirdevs; i++) { - int thisidx; - if ((thisidx = qemuDomainDeviceAliasIndex(&def->redirdevs[i]->info, "redir")) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to determine device index for redirected device")); - return -1; - } - if (thisidx >= idx) - idx = thisidx + 1; - } - } - - if (virAsprintf(&redirdev->info.alias, "redir%d", idx) < 0) - return -1; - return 0; -} - - -int -qemuAssignDeviceControllerAlias(virDomainDefPtr domainDef, - virQEMUCapsPtr qemuCaps, - virDomainControllerDefPtr controller) -{ - const char *prefix = virDomainControllerTypeToString(controller->type); - - if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { - if (!virQEMUCapsHasPCIMultiBus(qemuCaps, domainDef)) { - /* qemus that don't support multiple PCI buses have - * hardcoded the name of their single PCI controller as - * "pci". - */ - return VIR_STRDUP(controller->info.alias, "pci"); - } else if (controller->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) { - /* The pcie-root controller on Q35 machinetypes uses a - * different naming convention ("pcie.0"), because it is - * hardcoded that way in qemu. - */ - return virAsprintf(&controller->info.alias, "pcie.%d", controller->idx); - } - /* All other PCI controllers use the consistent "pci.%u" - * (including the hardcoded pci-root controller on - * multibus-capable qemus). - */ - return virAsprintf(&controller->info.alias, "pci.%d", controller->idx); - } else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE) { - /* for any machine based on e.g. I440FX or G3Beige, the - * first (and currently only) IDE controller is an integrated - * controller hardcoded with id "ide" - */ - if (qemuDomainMachineHasBuiltinIDE(domainDef) && - controller->idx == 0) - return VIR_STRDUP(controller->info.alias, "ide"); - } else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) { - /* for any Q35 machine, the first SATA controller is the - * integrated one, and it too is hardcoded with id "ide" - */ - if (qemuDomainMachineIsQ35(domainDef) && controller->idx == 0) - return VIR_STRDUP(controller->info.alias, "ide"); - } else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) { - /* first USB device is "usb", others are normal "usb%d" */ - if (controller->idx == 0) - return VIR_STRDUP(controller->info.alias, "usb"); - } - /* all other controllers use the default ${type}${index} naming - * scheme for alias/id. - */ - return virAsprintf(&controller->info.alias, "%s%d", prefix, controller->idx); -} - -static ssize_t -qemuGetNextChrDevIndex(virDomainDefPtr def, - virDomainChrDefPtr chr, - const char *prefix) -{ - const virDomainChrDef **arrPtr; - size_t cnt; - size_t i; - ssize_t idx = 0; - const char *prefix2 = NULL; - - if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE) - prefix2 = "serial"; - - virDomainChrGetDomainPtrs(def, chr->deviceType, &arrPtr, &cnt); - - for (i = 0; i < cnt; i++) { - ssize_t thisidx; - if (((thisidx = qemuDomainDeviceAliasIndex(&arrPtr[i]->info, prefix)) < 0) && - (prefix2 && - (thisidx = qemuDomainDeviceAliasIndex(&arrPtr[i]->info, prefix2)) < 0)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to determine device index for character device")); - return -1; - } - if (thisidx >= idx) - idx = thisidx + 1; - } - - return idx; -} - - -int -qemuAssignDeviceRNGAlias(virDomainRNGDefPtr rng, - size_t idx) -{ - if (virAsprintf(&rng->info.alias, "rng%zu", idx) < 0) - return -1; - - return 0; -} - - -int -qemuAssignDeviceChrAlias(virDomainDefPtr def, - virDomainChrDefPtr chr, - ssize_t idx) -{ - const char *prefix = NULL; - - switch ((virDomainChrDeviceType) chr->deviceType) { - case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: - prefix = "parallel"; - break; - - case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: - prefix = "serial"; - break; - - case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: - prefix = "console"; - break; - - case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: - prefix = "channel"; - break; - - case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: - return -1; - } - - if (idx == -1 && (idx = qemuGetNextChrDevIndex(def, chr, prefix)) < 0) - return -1; - - return virAsprintf(&chr->info.alias, "%s%zd", prefix, idx); -} - -int -qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) -{ - size_t i; - - for (i = 0; i < def->ndisks; i++) { - if (qemuAssignDeviceDiskAlias(def, def->disks[i], qemuCaps) < 0) - return -1; - } - for (i = 0; i < def->nnets; i++) { - /* type='hostdev' interfaces are also on the hostdevs list, - * and will have their alias assigned with other hostdevs. - */ - if (virDomainNetGetActualType(def->nets[i]) - != VIR_DOMAIN_NET_TYPE_HOSTDEV && - qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0) { - return -1; - } - } - - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) - return 0; - - for (i = 0; i < def->nfss; i++) { - if (virAsprintf(&def->fss[i]->info.alias, "fs%zu", i) < 0) - return -1; - } - for (i = 0; i < def->nsounds; i++) { - if (virAsprintf(&def->sounds[i]->info.alias, "sound%zu", i) < 0) - return -1; - } - for (i = 0; i < def->nhostdevs; i++) { - if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0) - return -1; - } - for (i = 0; i < def->nredirdevs; i++) { - if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0) - return -1; - } - for (i = 0; i < def->nvideos; i++) { - if (virAsprintf(&def->videos[i]->info.alias, "video%zu", i) < 0) - return -1; - } - for (i = 0; i < def->ncontrollers; i++) { - if (qemuAssignDeviceControllerAlias(def, qemuCaps, def->controllers[i]) < 0) - return -1; - } - for (i = 0; i < def->ninputs; i++) { - if (virAsprintf(&def->inputs[i]->info.alias, "input%zu", i) < 0) - return -1; - } - for (i = 0; i < def->nparallels; i++) { - if (qemuAssignDeviceChrAlias(def, def->parallels[i], i) < 0) - return -1; - } - for (i = 0; i < def->nserials; i++) { - if (qemuAssignDeviceChrAlias(def, def->serials[i], i) < 0) - return -1; - } - for (i = 0; i < def->nchannels; i++) { - if (qemuAssignDeviceChrAlias(def, def->channels[i], i) < 0) - return -1; - } - for (i = 0; i < def->nconsoles; i++) { - if (qemuAssignDeviceChrAlias(def, def->consoles[i], i) < 0) - return -1; - } - for (i = 0; i < def->nhubs; i++) { - if (virAsprintf(&def->hubs[i]->info.alias, "hub%zu", i) < 0) - return -1; - } - for (i = 0; i < def->nshmems; i++) { - if (virAsprintf(&def->shmems[i]->info.alias, "shmem%zu", i) < 0) - return -1; - } - for (i = 0; i < def->nsmartcards; i++) { - if (virAsprintf(&def->smartcards[i]->info.alias, "smartcard%zu", i) < 0) - return -1; - } - if (def->watchdog) { - if (virAsprintf(&def->watchdog->info.alias, "watchdog%d", 0) < 0) - return -1; - } - if (def->memballoon) { - if (virAsprintf(&def->memballoon->info.alias, "balloon%d", 0) < 0) - return -1; - } - for (i = 0; i < def->nrngs; i++) { - if (qemuAssignDeviceRNGAlias(def->rngs[i], i) < 0) - return -1; - } - if (def->tpm) { - if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0) - return -1; - } - for (i = 0; i < def->nmems; i++) { - if (virAsprintf(&def->mems[i]->info.alias, "dimm%zu", i) < 0) - return -1; - } - - return 0; -} - - static int qemuBuildDeviceAddressStr(virBufferPtr buf, virDomainDefPtr domainDef, diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 61a0212..ec45e15 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -226,22 +226,6 @@ char *qemuBuildRedirdevDevStr(virDomainDefPtr def, int qemuNetworkPrepareDevices(virDomainDefPtr def); -int qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps); -int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx); -int qemuAssignDeviceDiskAlias(virDomainDefPtr vmdef, - virDomainDiskDefPtr def, - virQEMUCapsPtr qemuCaps); -int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx); -int -qemuAssignDeviceControllerAlias(virDomainDefPtr domainDef, - virQEMUCapsPtr qemuCaps, - virDomainControllerDefPtr controller); -int qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx); -int qemuAssignDeviceChrAlias(virDomainDefPtr def, - virDomainChrDefPtr chr, - ssize_t idx); -int qemuAssignDeviceRNGAlias(virDomainRNGDefPtr rng, size_t idx); - int qemuGetDriveSourceString(virStorageSourcePtr src, virConnectPtr conn, char **source); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2bbc724..9405b65 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -48,6 +48,7 @@ #include "qemu_agent.h" #include "qemu_conf.h" #include "qemu_capabilities.h" +#include "qemu_assign_alias.h" #include "qemu_command.h" #include "qemu_parse_command.h" #include "qemu_cgroup.h" diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 71cfc79..f11e8ba 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -31,6 +31,7 @@ #include "qemu_command.h" #include "qemu_hostdev.h" #include "qemu_interface.h" +#include "qemu_assign_alias.h" #include "domain_audit.h" #include "netdev_bandwidth_conf.h" #include "domain_nwfilter.h" diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c4a15ac..f698392 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -39,6 +39,7 @@ #include "qemu_capabilities.h" #include "qemu_monitor.h" #include "qemu_command.h" +#include "qemu_assign_alias.h" #include "qemu_hostdev.h" #include "qemu_hotplug.h" #include "qemu_migration.h" diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 1f711dd..3e4233b 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -20,6 +20,7 @@ #include <config.h> #include "qemu/qemu_conf.h" +#include "qemu/qemu_assign_alias.h" #include "qemu/qemu_hotplug.h" #include "qemu/qemu_hotplugpriv.h" #include "qemumonitortestutils.h" diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 3c7693b..5b8421b 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -16,6 +16,7 @@ # include "viralloc.h" # include "qemu/qemu_capabilities.h" # include "qemu/qemu_command.h" +# include "qemu/qemu_assign_alias.h" # include "qemu/qemu_domain.h" # include "qemu/qemu_migration.h" # include "qemu/qemu_process.h" -- 2.5.0

On Mon, Feb 15, 2016 at 02:37:26PM -0500, John Ferlan wrote:
Create a new module qemu_assign_alias.c to handle the qemuAssign*Alias* APIs
qemu_alias.c is shorter and could contain that one function that extracts info from the alias.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/qemu/qemu_assign_alias.c | 468 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_assign_alias.h | 60 ++++++ src/qemu/qemu_command.c | 427 +-------------------------------------- src/qemu/qemu_command.h | 16 -- src/qemu/qemu_driver.c | 1 + src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_process.c | 1 + tests/qemuhotplugtest.c | 1 + tests/qemuxml2argvtest.c | 1 + 11 files changed, 536 insertions(+), 442 deletions(-) create mode 100644 src/qemu/qemu_assign_alias.c create mode 100644 src/qemu/qemu_assign_alias.h
ACK Jan

On 02/15/2016 02:37 PM, John Ferlan wrote:
Before disappearing too far down the rabbit hole...
My recent foray into qemu_command.c to split out qemu_parse_command.c got me thinking that the module could use quite a bit of a reorganization.
It's about 11,400 lines of all sorts of various functions. This started out as an attempt to order functions as they are used, but started turning into extracting certain API's/Helpers into other qemu_*.c modules.
There's still quite a bit to do, but rather than hold onto it too long I figured I'd send what I have so far now (and keep working through while waiting for reviews).
This is essentially all code motion with some function renames along the way.
John Ferlan (12): qemu-command: Move qemuBuildTPMBackendStr qemu-command: Move qemuVirCommandGetFDSet qemu-command: Move qemuBuildTPMDevStr qemu-command: Move qemuVirCommandGetDevSet qemu: Move qemuPhysIfaceConnect to qemu_interface.c and rename qemu: Move qemuNetworkIfaceConnect to qemu_interface.c and rename qemu-command: Move qemuDomainSupports* functions qemu-command: Move qemuDomain*Address* functions to qemu_domain.c qemu-command: Move remaining qemuDomain* functions qemu-command: Move and rename qemuOpenVhostNet qemu-command: Move qemuNetworkPrepareDevices qemu-command: Move qemuAssign*Alias* API's into their own module
po/POTFILES.in | 2 + src/Makefile.am | 1 + src/qemu/qemu_assign_alias.c | 468 +++++++ src/qemu/qemu_assign_alias.h | 60 + src/qemu/qemu_command.c | 3073 +++++------------------------------------- src/qemu/qemu_command.h | 48 - src/qemu/qemu_domain.c | 1646 ++++++++++++++++++++++ src/qemu/qemu_domain.h | 25 + src/qemu/qemu_driver.c | 1 + src/qemu/qemu_hotplug.c | 19 +- src/qemu/qemu_interface.c | 378 ++++++ src/qemu/qemu_interface.h | 22 + src/qemu/qemu_process.c | 56 +- tests/qemuhotplugtest.c | 1 + tests/qemuxml2argvtest.c | 1 + 15 files changed, 2978 insertions(+), 2823 deletions(-) create mode 100644 src/qemu/qemu_assign_alias.c create mode 100644 src/qemu/qemu_assign_alias.h
I changed all the prefixes from 'qemu-command:' to just 'qemu:' I modified patch 8 to create qemu_domain_address.{c,h} I modified patch 12 to rename from qemu_assign_alias.{c,h} to just qemu_alias.{c,h}... Also moved the qemuDomainDeviceAliasIndex in... I moved patched 9 to after patch 12 (simplified some things and then moved the functions into corresponding modules). Went back and made sure each step compiled, passed make check, and make syntax-check Pushed... Thanks for the review... On to the next pile of code motion... John
participants (3)
-
John Ferlan
-
Ján Tomko
-
Laine Stump