qemu: Make IOThread changing more robust

There are three APIs that allow changing IOThreads:

  virDomainAddIOThread()
  virDomainDelIOThread()
  virDomainSetIOThreadParams()

In case of QEMU driver these are handled by
qemuDomainChgIOThread() which attempts to be versatile enough to
work on both inactive and live domain definitions at the same
time. However, it's a bit clumsy - when a change to live
definition succeeds but fails in inactive definition then there's
no rollback. And somewhat rightfully so - changes to live
definition are in general harder to roll back. Therefore, do what
we do elsewhere (qemuDomainAttachDeviceLiveAndConfig(),
qemuDomainDetachDeviceAliasLiveAndConfig(), ...):

  1) do the change to inactive XML first,
  2) in fact, do the change to a copy of inactive XML,
  3) swap inactive XML and its copy only after everything
     succeeded.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
Michal Privoznik 2022-07-07 17:37:46 +02:00
parent 5aa2495854
commit 6db9c95a45

View File

@ -5595,6 +5595,7 @@ qemuDomainChgIOThread(virQEMUDriver *driver,
{
g_autoptr(virQEMUDriverConfig) cfg = NULL;
qemuDomainObjPrivate *priv;
g_autoptr(virDomainDef) defcopy = NULL;
virDomainDef *def;
virDomainDef *persistentDef;
virDomainIOThreadIDDef *iothreaddef = NULL;
@ -5610,6 +5611,55 @@ qemuDomainChgIOThread(virQEMUDriver *driver,
if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0)
goto endjob;
if (persistentDef) {
/* Make a copy of persistent definition and do all the changes there.
* Swap the definitions only after changes to live definition
* succeeded. */
if (!(defcopy = virDomainObjCopyPersistentDef(vm, driver->xmlopt,
priv->qemuCaps)))
return -1;
switch (action) {
case VIR_DOMAIN_IOTHREAD_ACTION_ADD:
if (virDomainDriverAddIOThreadCheck(defcopy, iothread.iothread_id) < 0)
goto endjob;
if (!virDomainIOThreadIDAdd(defcopy, iothread.iothread_id))
goto endjob;
break;
case VIR_DOMAIN_IOTHREAD_ACTION_DEL:
if (virDomainDriverDelIOThreadCheck(defcopy, iothread.iothread_id) < 0)
goto endjob;
virDomainIOThreadIDDel(defcopy, iothread.iothread_id);
break;
case VIR_DOMAIN_IOTHREAD_ACTION_MOD:
iothreaddef = virDomainIOThreadIDFind(defcopy, iothread.iothread_id);
if (!iothreaddef) {
virReportError(VIR_ERR_INVALID_ARG,
_("cannot find IOThread '%u' in iothreadids"),
iothread.iothread_id);
goto endjob;
}
if (qemuDomainIOThreadValidate(iothreaddef, iothread, false) < 0)
goto endjob;
if (qemuDomainHotplugModIOThreadIDDef(iothreaddef, iothread) < 0) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("configuring persistent polling values is not supported"));
goto endjob;
}
break;
}
}
if (def) {
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
@ -5660,50 +5710,12 @@ qemuDomainChgIOThread(virQEMUDriver *driver,
qemuDomainSaveStatus(vm);
}
if (persistentDef) {
switch (action) {
case VIR_DOMAIN_IOTHREAD_ACTION_ADD:
if (virDomainDriverAddIOThreadCheck(persistentDef, iothread.iothread_id) < 0)
goto endjob;
if (!virDomainIOThreadIDAdd(persistentDef, iothread.iothread_id))
goto endjob;
break;
case VIR_DOMAIN_IOTHREAD_ACTION_DEL:
if (virDomainDriverDelIOThreadCheck(persistentDef, iothread.iothread_id) < 0)
goto endjob;
virDomainIOThreadIDDel(persistentDef, iothread.iothread_id);
break;
case VIR_DOMAIN_IOTHREAD_ACTION_MOD:
iothreaddef = virDomainIOThreadIDFind(persistentDef, iothread.iothread_id);
if (!iothreaddef) {
virReportError(VIR_ERR_INVALID_ARG,
_("cannot find IOThread '%u' in iothreadids"),
iothread.iothread_id);
goto endjob;
}
if (qemuDomainIOThreadValidate(iothreaddef, iothread, false) < 0)
goto endjob;
if (qemuDomainHotplugModIOThreadIDDef(iothreaddef, iothread) < 0) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("configuring persistent polling values is not supported"));
goto endjob;
}
break;
}
if (virDomainDefSave(persistentDef, driver->xmlopt,
cfg->configDir) < 0)
/* Finally, if no error until here, we can save config. */
if (defcopy) {
if (virDomainDefSave(defcopy, driver->xmlopt, cfg->configDir) < 0)
goto endjob;
virDomainObjAssignDef(vm, &defcopy, false, NULL);
}
ret = 0;