diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fb454022170ebf8ee608837d98c9b48b5e34fc90..516a851d3d55b1a55769ac510bb44486a42290d2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4615,76 +4615,6 @@ static void qemuProcessEventHandler(void *data, void *opaque) } -static int -qemuDomainHotplugAddVcpu(virQEMUDriverPtr driver, - virDomainObjPtr vm, - unsigned int vcpu) -{ - virJSONValuePtr vcpuprops = NULL; - virDomainVcpuDefPtr vcpuinfo = virDomainDefGetVcpu(vm->def, vcpu); - qemuDomainVcpuPrivatePtr vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpuinfo); - unsigned int nvcpus = vcpupriv->vcpus; - bool newhotplug = qemuDomainSupportsNewVcpuHotplug(vm); - int ret = -1; - int rc; - int oldvcpus = virDomainDefGetVcpus(vm->def); - size_t i; - - if (newhotplug) { - if (virAsprintf(&vcpupriv->alias, "vcpu%u", vcpu) < 0) - goto cleanup; - - if (!(vcpuprops = qemuBuildHotpluggableCPUProps(vcpuinfo))) - goto cleanup; - } - - qemuDomainObjEnterMonitor(driver, vm); - - if (newhotplug) { - rc = qemuMonitorAddDeviceArgs(qemuDomainGetMonitor(vm), vcpuprops); - vcpuprops = NULL; - } else { - rc = qemuMonitorSetCPU(qemuDomainGetMonitor(vm), vcpu, true); - } - - if (qemuDomainObjExitMonitor(driver, vm) < 0) - goto cleanup; - - virDomainAuditVcpu(vm, oldvcpus, oldvcpus + nvcpus, "update", rc == 0); - - if (rc < 0) - goto cleanup; - - /* start outputting of the new XML element to allow keeping unpluggability */ - if (newhotplug) - vm->def->individualvcpus = true; - - if (qemuDomainRefreshVcpuInfo(driver, vm, QEMU_ASYNC_JOB_NONE, false) < 0) - goto cleanup; - - /* validation requires us to set the expected state prior to calling it */ - for (i = vcpu; i < vcpu + nvcpus; i++) { - vcpuinfo = virDomainDefGetVcpu(vm->def, i); - vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpuinfo); - - vcpuinfo->online = true; - - if (vcpupriv->tid > 0 && - qemuProcessSetupVcpu(vm, i) < 0) - goto cleanup; - } - - if (qemuDomainValidateVcpuInfo(vm) < 0) - goto cleanup; - - ret = 0; - - cleanup: - virJSONValueFree(vcpuprops); - return ret; -} - - static int qemuDomainSetVcpusAgent(virDomainObjPtr vm, unsigned int nvcpus) @@ -4779,275 +4709,6 @@ qemuDomainSetVcpusMax(virQEMUDriverPtr driver, } -/** - * qemuDomainSelectHotplugVcpuEntities: - * - * @def: domain definition - * @nvcpus: target vcpu count - * @enable: set to true if vcpus should be enabled - * - * Tries to find which vcpu entities need to be enabled or disabled to reach - * @nvcpus. This function works in order of the legacy hotplug but is able to - * skip over entries that are added out of order. - * - * Returns the bitmap of vcpus to modify on success, NULL on error. - */ -static virBitmapPtr -qemuDomainSelectHotplugVcpuEntities(virDomainDefPtr def, - unsigned int nvcpus, - bool *enable) -{ - virBitmapPtr ret = NULL; - virDomainVcpuDefPtr vcpu; - qemuDomainVcpuPrivatePtr vcpupriv; - unsigned int maxvcpus = virDomainDefGetVcpusMax(def); - unsigned int curvcpus = virDomainDefGetVcpus(def); - ssize_t i; - - if (!(ret = virBitmapNew(maxvcpus))) - return NULL; - - if (nvcpus > curvcpus) { - *enable = true; - - for (i = 0; i < maxvcpus && curvcpus < nvcpus; i++) { - vcpu = virDomainDefGetVcpu(def, i); - vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu); - - if (vcpu->online) - continue; - - if (vcpupriv->vcpus == 0) - continue; - - curvcpus += vcpupriv->vcpus; - - if (curvcpus > nvcpus) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("target vm vcpu granularity does not allow the " - "desired vcpu count")); - goto error; - } - - ignore_value(virBitmapSetBit(ret, i)); - } - } else { - *enable = false; - - for (i = maxvcpus - 1; i >= 0 && curvcpus > nvcpus; i--) { - vcpu = virDomainDefGetVcpu(def, i); - vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu); - - if (!vcpu->online) - continue; - - if (vcpupriv->vcpus == 0) - continue; - - if (!vcpupriv->alias) - continue; - - curvcpus -= vcpupriv->vcpus; - - if (curvcpus < nvcpus) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("target vm vcpu granularity does not allow the " - "desired vcpu count")); - goto error; - } - - ignore_value(virBitmapSetBit(ret, i)); - } - } - - if (curvcpus != nvcpus) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("failed to find appropriate hotpluggable vcpus to " - "reach the desired target vcpu count")); - goto error; - } - - return ret; - - error: - virBitmapFree(ret); - return NULL; -} - - -static int -qemuDomainSetVcpusLive(virQEMUDriverPtr driver, - virQEMUDriverConfigPtr cfg, - virDomainObjPtr vm, - virBitmapPtr vcpumap, - bool enable) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - qemuCgroupEmulatorAllNodesDataPtr emulatorCgroup = NULL; - ssize_t nextvcpu = -1; - int rc = 0; - int ret = -1; - - if (qemuCgroupEmulatorAllNodesAllow(priv->cgroup, &emulatorCgroup) < 0) - goto cleanup; - - if (enable) { - while ((nextvcpu = virBitmapNextSetBit(vcpumap, nextvcpu)) != -1) { - if ((rc = qemuDomainHotplugAddVcpu(driver, vm, nextvcpu)) < 0) - break; - } - } else { - for (nextvcpu = virDomainDefGetVcpusMax(vm->def) - 1; nextvcpu >= 0; nextvcpu--) { - if (!virBitmapIsBitSet(vcpumap, nextvcpu)) - continue; - - if ((rc = qemuDomainHotplugDelVcpu(driver, vm, nextvcpu)) < 0) - break; - } - } - - qemuDomainVcpuPersistOrder(vm->def); - - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) - goto cleanup; - - if (rc < 0) - goto cleanup; - - ret = 0; - - cleanup: - qemuCgroupEmulatorAllNodesRestore(emulatorCgroup); - - return ret; -} - - -/** - * qemuDomainSetVcpusConfig: - * @def: config/offline definition of a domain - * @nvcpus: target vcpu count - * - * Properly handle cold(un)plug of vcpus: - * - plug in inactive vcpus/uplug active rather than rewriting state - * - fix hotpluggable state - */ -static void -qemuDomainSetVcpusConfig(virDomainDefPtr def, - unsigned int nvcpus, - bool hotpluggable) -{ - virDomainVcpuDefPtr vcpu; - size_t curvcpus = virDomainDefGetVcpus(def); - size_t maxvcpus = virDomainDefGetVcpusMax(def); - size_t i; - - /* ordering information may become invalid, thus clear it */ - virDomainDefVcpuOrderClear(def); - - if (curvcpus == nvcpus) - return; - - if (curvcpus < nvcpus) { - for (i = 0; i < maxvcpus; i++) { - vcpu = virDomainDefGetVcpu(def, i); - - if (!vcpu) - continue; - - if (vcpu->online) { - /* non-hotpluggable vcpus need to be clustered at the beggining, - * thus we need to force vcpus to be hotpluggable when we find - * vcpus that are hotpluggable and online prior to the ones - * we are going to add */ - if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) - hotpluggable = true; - - continue; - } - - vcpu->online = true; - if (hotpluggable) { - vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; - def->individualvcpus = true; - } else { - vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO; - } - - if (++curvcpus == nvcpus) - break; - } - } else { - for (i = maxvcpus; i != 0; i--) { - vcpu = virDomainDefGetVcpu(def, i - 1); - - if (!vcpu || !vcpu->online) - continue; - - vcpu->online = false; - vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; - - if (--curvcpus == nvcpus) - break; - } - } -} - - -static int -qemuDomainSetVcpusInternal(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDefPtr def, - virDomainDefPtr persistentDef, - unsigned int nvcpus, - bool hotpluggable) -{ - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - virBitmapPtr vcpumap = NULL; - bool enable; - int ret = -1; - - if (def && nvcpus > virDomainDefGetVcpusMax(def)) { - virReportError(VIR_ERR_INVALID_ARG, - _("requested vcpus is greater than max allowable" - " vcpus for the live domain: %u > %u"), - nvcpus, virDomainDefGetVcpusMax(def)); - goto cleanup; - } - - if (persistentDef && nvcpus > virDomainDefGetVcpusMax(persistentDef)) { - virReportError(VIR_ERR_INVALID_ARG, - _("requested vcpus is greater than max allowable" - " vcpus for the persistent domain: %u > %u"), - nvcpus, virDomainDefGetVcpusMax(persistentDef)); - goto cleanup; - } - - if (def) { - if (!(vcpumap = qemuDomainSelectHotplugVcpuEntities(vm->def, nvcpus, - &enable))) - goto cleanup; - - if (qemuDomainSetVcpusLive(driver, cfg, vm, vcpumap, enable) < 0) - goto cleanup; - } - - if (persistentDef) { - qemuDomainSetVcpusConfig(persistentDef, nvcpus, hotpluggable); - - if (virDomainSaveConfig(cfg->configDir, driver->caps, persistentDef) < 0) - goto cleanup; - } - - ret = 0; - - cleanup: - virBitmapFree(vcpumap); - virObjectUnref(cfg); - return ret; -} - - static int qemuDomainSetVcpusFlags(virDomainPtr dom, unsigned int nvcpus, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index f2e98469e0f658c24b0f9543353eaa7f24e6fd1d..5b0a3f5155feccb62d4ffb2477801af2159bfffe 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5346,3 +5346,342 @@ qemuDomainHotplugDelVcpu(virQEMUDriverPtr driver, return qemuDomainRemoveVcpu(driver, vm, vcpu); } + + +static int +qemuDomainHotplugAddVcpu(virQEMUDriverPtr driver, + virDomainObjPtr vm, + unsigned int vcpu) +{ + virJSONValuePtr vcpuprops = NULL; + virDomainVcpuDefPtr vcpuinfo = virDomainDefGetVcpu(vm->def, vcpu); + qemuDomainVcpuPrivatePtr vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpuinfo); + unsigned int nvcpus = vcpupriv->vcpus; + bool newhotplug = qemuDomainSupportsNewVcpuHotplug(vm); + int ret = -1; + int rc; + int oldvcpus = virDomainDefGetVcpus(vm->def); + size_t i; + + if (newhotplug) { + if (virAsprintf(&vcpupriv->alias, "vcpu%u", vcpu) < 0) + goto cleanup; + + if (!(vcpuprops = qemuBuildHotpluggableCPUProps(vcpuinfo))) + goto cleanup; + } + + qemuDomainObjEnterMonitor(driver, vm); + + if (newhotplug) { + rc = qemuMonitorAddDeviceArgs(qemuDomainGetMonitor(vm), vcpuprops); + vcpuprops = NULL; + } else { + rc = qemuMonitorSetCPU(qemuDomainGetMonitor(vm), vcpu, true); + } + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto cleanup; + + virDomainAuditVcpu(vm, oldvcpus, oldvcpus + nvcpus, "update", rc == 0); + + if (rc < 0) + goto cleanup; + + /* start outputting of the new XML element to allow keeping unpluggability */ + if (newhotplug) + vm->def->individualvcpus = true; + + if (qemuDomainRefreshVcpuInfo(driver, vm, QEMU_ASYNC_JOB_NONE, false) < 0) + goto cleanup; + + /* validation requires us to set the expected state prior to calling it */ + for (i = vcpu; i < vcpu + nvcpus; i++) { + vcpuinfo = virDomainDefGetVcpu(vm->def, i); + vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpuinfo); + + vcpuinfo->online = true; + + if (vcpupriv->tid > 0 && + qemuProcessSetupVcpu(vm, i) < 0) + goto cleanup; + } + + if (qemuDomainValidateVcpuInfo(vm) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virJSONValueFree(vcpuprops); + return ret; +} + + +/** + * qemuDomainSelectHotplugVcpuEntities: + * + * @def: domain definition + * @nvcpus: target vcpu count + * @enable: set to true if vcpus should be enabled + * + * Tries to find which vcpu entities need to be enabled or disabled to reach + * @nvcpus. This function works in order of the legacy hotplug but is able to + * skip over entries that are added out of order. + * + * Returns the bitmap of vcpus to modify on success, NULL on error. + */ +static virBitmapPtr +qemuDomainSelectHotplugVcpuEntities(virDomainDefPtr def, + unsigned int nvcpus, + bool *enable) +{ + virBitmapPtr ret = NULL; + virDomainVcpuDefPtr vcpu; + qemuDomainVcpuPrivatePtr vcpupriv; + unsigned int maxvcpus = virDomainDefGetVcpusMax(def); + unsigned int curvcpus = virDomainDefGetVcpus(def); + ssize_t i; + + if (!(ret = virBitmapNew(maxvcpus))) + return NULL; + + if (nvcpus > curvcpus) { + *enable = true; + + for (i = 0; i < maxvcpus && curvcpus < nvcpus; i++) { + vcpu = virDomainDefGetVcpu(def, i); + vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu); + + if (vcpu->online) + continue; + + if (vcpupriv->vcpus == 0) + continue; + + curvcpus += vcpupriv->vcpus; + + if (curvcpus > nvcpus) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("target vm vcpu granularity does not allow the " + "desired vcpu count")); + goto error; + } + + ignore_value(virBitmapSetBit(ret, i)); + } + } else { + *enable = false; + + for (i = maxvcpus - 1; i >= 0 && curvcpus > nvcpus; i--) { + vcpu = virDomainDefGetVcpu(def, i); + vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu); + + if (!vcpu->online) + continue; + + if (vcpupriv->vcpus == 0) + continue; + + if (!vcpupriv->alias) + continue; + + curvcpus -= vcpupriv->vcpus; + + if (curvcpus < nvcpus) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("target vm vcpu granularity does not allow the " + "desired vcpu count")); + goto error; + } + + ignore_value(virBitmapSetBit(ret, i)); + } + } + + if (curvcpus != nvcpus) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("failed to find appropriate hotpluggable vcpus to " + "reach the desired target vcpu count")); + goto error; + } + + return ret; + + error: + virBitmapFree(ret); + return NULL; +} + + +static int +qemuDomainSetVcpusLive(virQEMUDriverPtr driver, + virQEMUDriverConfigPtr cfg, + virDomainObjPtr vm, + virBitmapPtr vcpumap, + bool enable) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuCgroupEmulatorAllNodesDataPtr emulatorCgroup = NULL; + ssize_t nextvcpu = -1; + int rc = 0; + int ret = -1; + + if (qemuCgroupEmulatorAllNodesAllow(priv->cgroup, &emulatorCgroup) < 0) + goto cleanup; + + if (enable) { + while ((nextvcpu = virBitmapNextSetBit(vcpumap, nextvcpu)) != -1) { + if ((rc = qemuDomainHotplugAddVcpu(driver, vm, nextvcpu)) < 0) + break; + } + } else { + for (nextvcpu = virDomainDefGetVcpusMax(vm->def) - 1; nextvcpu >= 0; nextvcpu--) { + if (!virBitmapIsBitSet(vcpumap, nextvcpu)) + continue; + + if ((rc = qemuDomainHotplugDelVcpu(driver, vm, nextvcpu)) < 0) + break; + } + } + + qemuDomainVcpuPersistOrder(vm->def); + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) + goto cleanup; + + if (rc < 0) + goto cleanup; + + ret = 0; + + cleanup: + qemuCgroupEmulatorAllNodesRestore(emulatorCgroup); + + return ret; +} + + +/** + * qemuDomainSetVcpusConfig: + * @def: config/offline definition of a domain + * @nvcpus: target vcpu count + * + * Properly handle cold(un)plug of vcpus: + * - plug in inactive vcpus/uplug active rather than rewriting state + * - fix hotpluggable state + */ +static void +qemuDomainSetVcpusConfig(virDomainDefPtr def, + unsigned int nvcpus, + bool hotpluggable) +{ + virDomainVcpuDefPtr vcpu; + size_t curvcpus = virDomainDefGetVcpus(def); + size_t maxvcpus = virDomainDefGetVcpusMax(def); + size_t i; + + /* ordering information may become invalid, thus clear it */ + virDomainDefVcpuOrderClear(def); + + if (curvcpus == nvcpus) + return; + + if (curvcpus < nvcpus) { + for (i = 0; i < maxvcpus; i++) { + vcpu = virDomainDefGetVcpu(def, i); + + if (!vcpu) + continue; + + if (vcpu->online) { + /* non-hotpluggable vcpus need to be clustered at the beggining, + * thus we need to force vcpus to be hotpluggable when we find + * vcpus that are hotpluggable and online prior to the ones + * we are going to add */ + if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) + hotpluggable = true; + + continue; + } + + vcpu->online = true; + if (hotpluggable) { + vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; + def->individualvcpus = true; + } else { + vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO; + } + + if (++curvcpus == nvcpus) + break; + } + } else { + for (i = maxvcpus; i != 0; i--) { + vcpu = virDomainDefGetVcpu(def, i - 1); + + if (!vcpu || !vcpu->online) + continue; + + vcpu->online = false; + vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; + + if (--curvcpus == nvcpus) + break; + } + } +} + + +int +qemuDomainSetVcpusInternal(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDefPtr def, + virDomainDefPtr persistentDef, + unsigned int nvcpus, + bool hotpluggable) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virBitmapPtr vcpumap = NULL; + bool enable; + int ret = -1; + + if (def && nvcpus > virDomainDefGetVcpusMax(def)) { + virReportError(VIR_ERR_INVALID_ARG, + _("requested vcpus is greater than max allowable" + " vcpus for the live domain: %u > %u"), + nvcpus, virDomainDefGetVcpusMax(def)); + goto cleanup; + } + + if (persistentDef && nvcpus > virDomainDefGetVcpusMax(persistentDef)) { + virReportError(VIR_ERR_INVALID_ARG, + _("requested vcpus is greater than max allowable" + " vcpus for the persistent domain: %u > %u"), + nvcpus, virDomainDefGetVcpusMax(persistentDef)); + goto cleanup; + } + + if (def) { + if (!(vcpumap = qemuDomainSelectHotplugVcpuEntities(vm->def, nvcpus, + &enable))) + goto cleanup; + + if (qemuDomainSetVcpusLive(driver, cfg, vm, vcpumap, enable) < 0) + goto cleanup; + } + + if (persistentDef) { + qemuDomainSetVcpusConfig(persistentDef, nvcpus, hotpluggable); + + if (virDomainSaveConfig(cfg->configDir, driver->caps, persistentDef) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virBitmapFree(vcpumap); + virObjectUnref(cfg); + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 85ec72470594d8318f4b91f6b400001db1e69997..13242eec9a6ec055663a9113ac45271d569a5ba3 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -136,4 +136,11 @@ bool qemuDomainSignalDeviceRemoval(virDomainObjPtr vm, const char *devAlias, qemuDomainUnpluggingDeviceStatus status); +int qemuDomainSetVcpusInternal(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDefPtr def, + virDomainDefPtr persistentDef, + unsigned int nvcpus, + bool hotpluggable); + #endif /* __QEMU_HOTPLUG_H__ */