qemu: Move cpu hotplug code into qemu_hotplug.c

Move all the worker code into the appropriate file. This will also allow
testing of cpu hotplug.
This commit is contained in:
Peter Krempa 2016-11-24 16:56:56 +01:00
parent 5570f26763
commit 9d14cf595a
3 changed files with 346 additions and 339 deletions

View File

@ -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,

View File

@ -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;
}

View File

@ -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__ */