[libvirt] [PATCH v2 20/23] qemu: command: Add support for sparse vcpu topologies

John Ferlan jferlan at redhat.com
Fri Aug 19 21:11:53 UTC 2016



On 08/19/2016 10:38 AM, Peter Krempa wrote:
> Add support for using the new approach to hotplug vcpus using device_add
> during startup of qemu to allow sparse vcpu topologies.
> 
> There are a few limitations imposed by qemu on the supported
> configuration:
> - vcpu0 needs to be always present and not hotpluggable
> - non-hotpluggable cpus need to be ordered at the beginning
> - order of the vcpus needs to be unique for every single hotpluggable
>   entity
> 
> Qemu also doesn't really allow to query the information necessary to
> start a VM with the vcpus directly on the commandline. Fortunately they
> can be hotplugged during startup.
> 
> The new hotplug code uses the following approach:
> - non-hotpluggable vcpus are counted and put to the -smp option
> - qemu is started
> - qemu is queried for the necessary information
> - the configuration is checked
> - the hotpluggable vcpus are hotplugged
> - vcpus are started
> 

Should these "rules" be listed in the html somewhere?  Certainly could
be challenging for the first few people to try and set something up... I
it seems the "order" (from the XML) only matters if online, true?

> This patch adds a lot of checking code and enables the support to
> specify the individual vcpu element with qemu.
> ---
>  src/qemu/qemu_command.c                            |  20 ++-
>  src/qemu/qemu_domain.c                             |  76 ++++++++-
>  src/qemu/qemu_process.c                            | 173 +++++++++++++++++++++
>  .../qemuxml2argv-cpu-hotplug-startup.args          |  20 +++
>  .../qemuxml2argv-cpu-hotplug-startup.xml           |  29 ++++
>  tests/qemuxml2argvtest.c                           |   2 +
>  6 files changed, 315 insertions(+), 5 deletions(-)
>  create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.args
>  create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.xml
> 
> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
> index 28e5a7e..c1dc390 100644
> --- a/src/qemu/qemu_command.c
> +++ b/src/qemu/qemu_command.c
> @@ -7082,17 +7082,29 @@ qemuBuildMachineCommandLine(virCommandPtr cmd,
> 
>  static int
>  qemuBuildSmpCommandLine(virCommandPtr cmd,
> -                        const virDomainDef *def)
> +                        virDomainDefPtr def)
>  {
>      char *smp;
>      virBuffer buf = VIR_BUFFER_INITIALIZER;
> +    unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
> +    unsigned int nvcpus = 0;
> +    virDomainVcpuDefPtr vcpu;
> +    size_t i;
> +
> +    /* count un-hotpluggable enabled vcpus. Hotpluggable ones will be added
> +     * in a different way */
> +    for (i = 0; i < maxvcpus; i++) {
> +        vcpu = virDomainDefGetVcpu(def, i);
> +        if (vcpu->online && vcpu->hotpluggable == VIR_TRISTATE_BOOL_NO)
> +            nvcpus++;
> +    }
> 
>      virCommandAddArg(cmd, "-smp");
> 
> -    virBufferAsprintf(&buf, "%u", virDomainDefGetVcpus(def));
> +    virBufferAsprintf(&buf, "%u", nvcpus);
> 
> -    if (virDomainDefHasVcpusOffline(def))
> -        virBufferAsprintf(&buf, ",maxcpus=%u", virDomainDefGetVcpusMax(def));
> +    if (nvcpus != maxvcpus)
> +        virBufferAsprintf(&buf, ",maxcpus=%u", maxvcpus);
>      /* sockets, cores, and threads are either all zero
>       * or all non-zero, thus checking one of them is enough */
>      if (def->cpu && def->cpu->sockets) {
> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index aa93498..1602824 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -2253,6 +2253,76 @@ qemuDomainRecheckInternalPaths(virDomainDefPtr def,
> 
> 
>  static int
> +qemuDomainDefVcpusPostParse(virDomainDefPtr def)
> +{
> +    unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
> +    virDomainVcpuDefPtr vcpu;
> +    virDomainVcpuDefPtr prevvcpu;
> +    size_t i;
> +    bool has_order = false;
> +
> +    /* vcpu 0 needs to be present, first, and non-hotpluggable */
> +    vcpu = virDomainDefGetVcpu(def, 0);
> +    if (!vcpu->online) {
> +        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                       _("vcpu 0 can't be offline"));
> +        return -1;
> +    }
> +    if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
> +        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                       _("vcpu0 can't be hotpluggable"));
> +        return -1;
> +    }
> +    if (vcpu->order != 0 && vcpu->order != 1) {
> +        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                       _("vcpu0 must be enabled first"));
> +        return -1;
> +    }
> +
> +    if (vcpu->order != 0)
> +        has_order = true;
> +
> +    prevvcpu = vcpu;
> +
> +    /* all online vcpus or no online vcpu need to have order set */

s/no online/non online/ ?

> +    for (i = 1; i < maxvcpus; i++) {
> +        vcpu = virDomainDefGetVcpu(def, i);
> +
> +        if (vcpu->online &&
> +            (vcpu->order != 0) != has_order) {
> +            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                           _("all vcpus must have either set or unset order"));
> +            return -1;
> +        }
> +
> +        /* few conditions for non-hotpluggable (thus onine) vcpus */

s/onine/online

> +        if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_NO) {
> +            /* they can be ordered only at the beinning */

s/beinning/beginning

> +            if (prevvcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
> +                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                               _("online non-hotpluggable vcpus need to be at "
> +                                 "the beginning"));

"need to be ordered prior to hotplugable vcpus"

?  Yours works, just trying to work "order" in there since that's what's
important.

> +                return -1;
> +            }
> +
> +            /* they need to be in order (qemu doesn't support any order yet).
> +             * Also note that multiple vcpus may share order on some platforms */
> +            if (prevvcpu->order > vcpu->order) {
> +                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> +                               _("online non-hotpluggable vcpus must be ordered "
> +                                 "in ascending order"));
> +                return -1;
> +            }
> +        }
> +
> +        prevvcpu = vcpu;
> +    }
> +
> +    return 0;
> +}
> +
> +
> +static int
>  qemuDomainDefPostParse(virDomainDefPtr def,
>                         virCapsPtr caps,
>                         unsigned int parseFlags,
> @@ -2307,6 +2377,9 @@ qemuDomainDefPostParse(virDomainDefPtr def,
>      if (virSecurityManagerVerify(driver->securityManager, def) < 0)
>          goto cleanup;
> 
> +    if (qemuDomainDefVcpusPostParse(def) < 0)
> +        goto cleanup;
> +
>      ret = 0;
>   cleanup:
>      virObjectUnref(qemuCaps);
> @@ -2709,7 +2782,8 @@ virDomainDefParserConfig virQEMUDriverDomainDefParserConfig = {
>      .deviceValidateCallback = qemuDomainDeviceDefValidate,
> 
>      .features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG |
> -                VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN
> +                VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN |
> +                VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS,
>  };
> 
> 
> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
> index f3915a5..c31d2ad 100644
> --- a/src/qemu/qemu_process.c
> +++ b/src/qemu/qemu_process.c
> @@ -4760,6 +4760,167 @@ qemuProcessSetupIOThreads(virDomainObjPtr vm)
>  }
> 
> 
> +static int
> +qemuProcessValidateHotpluggableVcpus(virDomainDefPtr def)
> +{
> +    virDomainVcpuDefPtr vcpu;
> +    virDomainVcpuDefPtr subvcpu;
> +    qemuDomainVcpuPrivatePtr vcpupriv;
> +    unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
> +    size_t i = 0;
> +    size_t j;
> +    virBitmapPtr ordermap = NULL;
> +    int ret = -1;
> +
> +    if (!(ordermap = virBitmapNew(maxvcpus)))
> +        goto cleanup;
> +
> +    /* validate:
> +     * - all hotpluggable entities to be hotplugged have the correct data
> +     * - vcpus belonging to a hotpluggable entity share configuration
> +     * - order of the hotpluggable entities is unique
> +     */
> +    for (i = 0; i < maxvcpus; i++) {
> +        vcpu = virDomainDefGetVcpu(def, i);
> +        vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu);
> +
> +        /* skip over hotpluggable entities  */
> +        if (vcpupriv->vcpus == 0)
> +            continue;
> +
> +        if (vcpu->order != 0) {
> +            if (virBitmapIsBitSet(ordermap, vcpu->order - 1)) {
> +                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> +                               _("duplicate vcpu order '%u'"), vcpu->order - 1);
> +                goto cleanup;
> +            }
> +
> +            ignore_value(virBitmapSetBit(ordermap, vcpu->order - 1));
> +        }
> +
> +
> +        for (j = i + 1; j < (i + vcpupriv->vcpus); j++) {
> +            subvcpu = virDomainDefGetVcpu(def, j);
> +            if (subvcpu->hotpluggable != vcpu->hotpluggable ||
> +                subvcpu->online != vcpu->online ||
> +                subvcpu->order != vcpu->order) {
> +                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> +                               _("vcpus '%zu' and '%zu' are in the same hotplug "
> +                                 "group but differ in configuration"), i, j);
> +                goto cleanup;
> +            }
> +        }
> +
> +        if (vcpu->online && vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
> +            if ((vcpupriv->socket_id == -1 && vcpupriv->core_id == -1 &&
> +                 vcpupriv->thread_id == -1) ||
> +                !vcpupriv->type) {
> +                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> +                               _("vcpu '%zu' is missing hotplug data"), i);
> +                goto cleanup;
> +            }
> +        }
> +    }
> +
> +    ret = 0;
> + cleanup:
> +    virBitmapFree(ordermap);
> +    return ret;
> +}
> +
> +
> +static int
> +qemuDomainHasHotpluggableStartupVcpus(virDomainDefPtr def)
> +{
> +    size_t maxvcpus = virDomainDefGetVcpusMax(def);
> +    virDomainVcpuDefPtr vcpu;
> +    size_t i;
> +
> +    for (i = 0; i < maxvcpus; i++) {
> +        vcpu = virDomainDefGetVcpu(def, i);
> +
> +        if (vcpu->online && vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES)
> +            return true;
> +    }
> +
> +    return false;
> +}
> +
> +
> +static int
> +qemuProcessVcpusSortOrder(const void *a,
> +                          const void *b)
> +{
> +    const virDomainVcpuDef *vcpua = a;
> +    const virDomainVcpuDef *vcpub = b;
> +
> +    return vcpua->order - vcpub->order;
> +}
> +
> +
> +static int
> +qemuProcessSetupHotpluggableVcpus(virQEMUDriverPtr driver,
> +                                  virDomainObjPtr vm,
> +                                  qemuDomainAsyncJob asyncJob)
> +{
> +    unsigned int maxvcpus = virDomainDefGetVcpusMax(vm->def);
> +    virDomainVcpuDefPtr vcpu;
> +    qemuDomainVcpuPrivatePtr vcpupriv;
> +    virJSONValuePtr vcpuprops = NULL;
> +    size_t i;
> +    int ret = -1;
> +    int rc;
> +
> +    virDomainVcpuDefPtr *bootHotplug = NULL;
> +    size_t nbootHotplug = 0;
> +
> +    for (i = 0; i < maxvcpus; i++) {
> +        vcpu = virDomainDefGetVcpu(vm->def, i);
> +        vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu);
> +
> +        if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES && vcpu->online &&
> +            vcpupriv->vcpus != 0) {
> +            if (virAsprintf(&vcpupriv->alias, "vcpu%zu", i) < 0)
> +                goto cleanup;

Is it possible the ->alias is already filled in?? Just checking we
cannot overwrite here... I don't believe so since this is called at
Launch only...

> +
> +            if (VIR_APPEND_ELEMENT(bootHotplug, nbootHotplug, vcpu) < 0)
> +                goto cleanup;
> +        }
> +    }

Yet another "roll your eyes" Coverity complaint...

if nbootHotplug == 0, then bootHotplug == NULL, which is bad for qsort.

[it was the only new one too]

John
> +
> +    qsort(bootHotplug, nbootHotplug, sizeof(*bootHotplug),
> +          qemuProcessVcpusSortOrder);
> +
> +    for (i = 0; i < nbootHotplug; i++) {
> +        vcpu = bootHotplug[i];
> +
> +        if (!(vcpuprops = qemuBuildHotpluggableCPUProps(vcpu)))
> +            goto cleanup;
> +
> +        if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
> +            goto cleanup;
> +
> +        rc = qemuMonitorAddDeviceArgs(qemuDomainGetMonitor(vm), vcpuprops);
> +        vcpuprops = NULL;
> +
> +        if (qemuDomainObjExitMonitor(driver, vm) < 0)
> +            goto cleanup;
> +
> +        if (rc < 0)
> +            goto cleanup;
> +
> +        virJSONValueFree(vcpuprops);
> +    }
> +
> +    ret = 0;
> +
> + cleanup:
> +    VIR_FREE(bootHotplug);
> +    virJSONValueFree(vcpuprops);
> +    return ret;
> +}
> +
> +
>  /**
>   * qemuProcessPrepareDomain
>   *
> @@ -5236,6 +5397,18 @@ qemuProcessLaunch(virConnectPtr conn,
>      if (qemuSetupCpusetMems(vm) < 0)
>          goto cleanup;
> 
> +    VIR_DEBUG("setting up hotpluggable cpus");
> +    if (qemuDomainHasHotpluggableStartupVcpus(vm->def)) {
> +        if (qemuDomainRefreshVcpuInfo(driver, vm, asyncJob, false) < 0)
> +            goto cleanup;
> +
> +        if (qemuProcessValidateHotpluggableVcpus(vm->def) < 0)
> +            goto cleanup;
> +
> +        if (qemuProcessSetupHotpluggableVcpus(driver, vm, asyncJob) < 0)
> +            goto cleanup;
> +    }
> +
>      VIR_DEBUG("Refreshing VCPU info");
>      if (qemuDomainRefreshVcpuInfo(driver, vm, asyncJob, false) < 0)
>          goto cleanup;
> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.args b/tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.args
> new file mode 100644
> index 0000000..035f250
> --- /dev/null
> +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.args
> @@ -0,0 +1,20 @@
> +LC_ALL=C \
> +PATH=/bin \
> +HOME=/home/test \
> +USER=test \
> +LOGNAME=test \
> +QEMU_AUDIO_DRV=none \
> +/usr/bin/qemu \
> +-name QEMUGuest1 \
> +-S \
> +-M pc \
> +-m 214 \
> +-smp 1,maxcpus=6,sockets=3,cores=2,threads=1 \
> +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
> +-nographic \
> +-nodefaults \
> +-monitor unix:/tmp/lib/domain--1-QEMUGuest1/monitor.sock,server,nowait \
> +-no-acpi \
> +-boot n \
> +-usb \
> +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.xml b/tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.xml
> new file mode 100644
> index 0000000..58718aa
> --- /dev/null
> +++ b/tests/qemuxml2argvdata/qemuxml2argv-cpu-hotplug-startup.xml
> @@ -0,0 +1,29 @@
> +<domain type='qemu'>
> +  <name>QEMUGuest1</name>
> +  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
> +  <memory unit='KiB'>219100</memory>
> +  <currentMemory unit='KiB'>219100</currentMemory>
> +  <vcpu placement='static' current='3'>6</vcpu>
> +  <vcpus>
> +    <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>
> +    <vcpu id='1' enabled='no' hotpluggable='yes'/>
> +    <vcpu id='2' enabled='no' hotpluggable='yes'/>
> +    <vcpu id='3' enabled='no' hotpluggable='yes'/>
> +    <vcpu id='4' enabled='yes' hotpluggable='yes' order='2'/>
> +    <vcpu id='5' enabled='yes' hotpluggable='yes' order='3'/>
> +  </vcpus>
> +  <os>
> +    <type arch='x86_64' machine='pc'>hvm</type>
> +    <boot dev='network'/>
> +  </os>
> +  <cpu>
> +    <topology sockets="3" cores="2" threads="1"/>
> +  </cpu>
> +  <clock offset='utc'/>
> +  <on_poweroff>destroy</on_poweroff>
> +  <on_reboot>restart</on_reboot>
> +  <on_crash>destroy</on_crash>
> +  <devices>
> +      <emulator>/usr/bin/qemu</emulator>
> +  </devices>
> +</domain>
> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
> index e8b8cb4..39abe72 100644
> --- a/tests/qemuxml2argvtest.c
> +++ b/tests/qemuxml2argvtest.c
> @@ -2106,6 +2106,8 @@ mymain(void)
>      DO_TEST("intel-iommu", QEMU_CAPS_DEVICE_PCI_BRIDGE,
>              QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE, QEMU_CAPS_DEVICE_INTEL_IOMMU);
> 
> +    DO_TEST("cpu-hotplug-startup", QEMU_CAPS_QUERY_HOTPLUGGABLE_CPUS);
> +
>      qemuTestDriverFree(&driver);
> 
>      return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
> 




More information about the libvir-list mailing list