[libvirt] [PATCH v2 8/9] qemu: Add support to Add/Delete IOThreads
Peter Krempa
pkrempa at redhat.com
Mon Apr 13 13:28:08 UTC 2015
On Fri, Apr 10, 2015 at 17:36:26 -0400, John Ferlan wrote:
> Add qemuDomainAddIOThread and qemuDomainDelIOThread in order to add or
> remove an IOThread to/from the host either for live or config optoins
>
> The implementation for the 'live' option will use the iothreadpids list
> in order to make decision, while the 'config' option will use the
> iothreadids list. Additionally, for deletion each may have to adjust
> the iothreadpin list.
>
> IOThreads are implemented by qmp objects, the code makes use of the existing
> qemuMonitorAddObject or qemuMonitorDelObject APIs.
>
> Signed-off-by: John Ferlan <jferlan at redhat.com>
> ---
> src/conf/domain_audit.c | 9 +
> src/conf/domain_audit.h | 6 +
> src/libvirt_private.syms | 1 +
> src/qemu/qemu_driver.c | 432 +++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 448 insertions(+)
>
...
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index d99f886..5b0784f 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -6186,6 +6186,436 @@ qemuDomainPinIOThread(virDomainPtr dom,
> return ret;
> }
>
> +static int
> +qemuDomainHotplugIOThread(virQEMUDriverPtr driver,
> + virDomainObjPtr vm,
> + unsigned int iothread_id,
> + const char *name,
> + bool add)
> +{
> + qemuDomainObjPrivatePtr priv = vm->privateData;
> + char *alias = NULL;
> + size_t i, idx;
> + int rc = -1;
> + int ret = -1;
> + unsigned int orig_niothreads = vm->def->iothreads;
> + unsigned int exp_niothreads = vm->def->iothreads;
> + int new_niothreads = 0;
> + qemuMonitorIOThreadInfoPtr *new_iothreads = NULL;
> + virCgroupPtr cgroup_iothread = NULL;
> + char *mem_mask = NULL;
> +
> + /* Let's see if we can find this iothread_id in our iothreadpids list
> + * For add finding the same iothread_id will cause a failure since we
> + * cannot add the same iothread_id twice.
> + * For del finding our iothread_id is good since we cannot delete
> + * something that doesn't exist
> + */
The comment states obvious facts.
> + for (idx = 0; idx < priv->niothreadpids; idx++) {
> + if (iothread_id == priv->iothreadpids[idx]->iothread_id)
> + break;
> + }
> +
> + if (add) {
> + if (idx < priv->niothreadpids) {
> + virReportError(VIR_ERR_INVALID_ARG,
> + _("an IOThread is already using iothread_id "
> + "'%u' in iothreadpids"),
> + iothread_id);
> + goto cleanup;
> + }
> + } else {
> + if (idx == priv->niothreadpids) {
> + virReportError(VIR_ERR_INVALID_ARG,
> + _("cannot find IOThread '%u' in iothreadpids"),
> + iothread_id);
> + goto cleanup;
> + }
> +
> + /* The qemuDomainDelThread doesn't pass (or need to pass) the
> + * name. So we'll grab it here so that we can formulate the
> + * correct alias for qemuMonitorDelObject to find this object.
> + */
With the changes I've suggested this won't be necessary
> + name = priv->iothreadpids[idx]->name;
> + }
> +
> + /* Generate alias */
> + if (name) {
> + if (virAsprintf(&alias, "%s_iothread%u", name, iothread_id) < 0)
> + return -1;
> + } else {
> + if (virAsprintf(&alias, "iothread%u", iothread_id) < 0)
> + return -1;
> + }
Neither this.
> +
> + qemuDomainObjEnterMonitor(driver, vm);
> +
> + if (!virDomainObjIsActive(vm)) {
> + virReportError(VIR_ERR_OPERATION_INVALID, "%s",
> + _("cannot change IOThreads for an inactive domain"));
> + goto exit_monitor;
> + }
This is a bit too late to check if the domain is active.
> +
> + if (add) {
> + rc = qemuMonitorAddObject(priv->mon, "iothread", alias, NULL);
> + exp_niothreads++;
> + } else {
> + rc = qemuMonitorDelObject(priv->mon, alias);
> + exp_niothreads--;
> + }
> +
> + if (rc < 0)
> + goto exit_monitor;
> +
> + /* After hotplugging the IOThreads we need to re-detect the
> + * IOThreads thread_id's, adjust the cgroups, thread affinity,
> + * and the priv->iothreadpids list.
> + */
> + if ((new_niothreads = qemuMonitorGetIOThreads(priv->mon,
> + &new_iothreads)) < 0) {
> + virResetLastError();
> + goto exit_monitor;
In this case you'd report an empty error as exit_monitor leads to
returning -1 without reporting any new one.
> + }
> +
> + if (qemuDomainObjExitMonitor(driver, vm) < 0)
> + goto cleanup;
> +
> + /* ohhh something went wrong */
Obvious.
> + if (new_niothreads != exp_niothreads) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("got wrong number of IOThread ids from QEMU monitor. "
> + "got %d, wanted %d"),
> + new_niothreads, exp_niothreads);
> + vm->def->iothreads = new_niothreads;
> + goto cleanup;
> + }
> + vm->def->iothreads = exp_niothreads;
> +
> + if (virDomainNumatuneGetMode(vm->def->numa, -1) ==
> + VIR_DOMAIN_NUMATUNE_MEM_STRICT &&
> + virDomainNumatuneMaybeFormatNodeset(vm->def->numa,
> + priv->autoNodeset,
> + &mem_mask, -1) < 0)
> + goto cleanup;
We really should store the above data somewhere. I've seen the above
construct a few times in different places lately.
> +
> +
> + if (add) {
> + qemuDomainIOThreadInfoPtr info;
> + unsigned int idval = 0;
> + char *idname = NULL;
> + int thread_id;
> +
> + /*
> + * If we've successfully added an IOThread, find out where we added it
> + * in the new IOThread list, so we can add it to our iothreadpids list
> + */
> + for (i = 0; i < new_niothreads; i++) {
> +
> + if (qemuDomainParseIOThreadAlias(new_iothreads[i]->name,
> + &idval, &idname) < 0)
> + goto cleanup;
You already know the data as you've formated it a few lines above.
> +
> + if (iothread_id == idval)
> + break;
> +
> + VIR_FREE(idname);
> + }
> +
> + /* something else went wrong */
...
> + if (idval != iothread_id) {
> + VIR_FREE(idname);
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("cannot find new IOThread '%u' in QEMU monitor."),
> + iothread_id);
> + goto cleanup;
> + }
> + thread_id = new_iothreads[i]->thread_id;
> +
> + if (VIR_ALLOC(info) < 0) {
> + VIR_FREE(idname);
> + goto cleanup;
> + }
> + info->thread_id = thread_id;
> + info->iothread_id = iothread_id;
> + info->name = idname;
> + if (VIR_APPEND_ELEMENT(priv->iothreadpids,
> + priv->niothreadpids, info) < 0) {
> + VIR_FREE(info->name);
> + VIR_FREE(info);
> + goto cleanup;
> + }
> +
> + /* Add IOThread to cgroup if present */
> + if (priv->cgroup) {
> + cgroup_iothread =
> + qemuDomainAddCgroupForThread(priv->cgroup,
> + VIR_CGROUP_THREAD_IOTHREAD,
> + iothread_id, mem_mask, thread_id);
> + if (!cgroup_iothread)
> + // Do we remove from iothreadpids?
The coding guidelines state that the old style comments should be used
rather than //
> + goto cleanup;
> + }
> +
> + /* Inherit def->cpuset */
> + if (vm->def->cpumask) {
> + if (qemuDomainHotplugAddPin(vm->def->cpumask, iothread_id,
> + &vm->def->cputune.iothreadspin,
> + &vm->def->cputune.niothreadspin) < 0)
> +
> + // Do we remove from iothreadpids && scratch the cgroup?
...
> + goto cleanup;
> +
> + if (qemuDomainHotplugPinThread(vm->def->cpumask, iothread_id,
> + thread_id, cgroup_iothread) < 0)
> + // Do we remove from iothreadpids, iothreadspin, and cgroup
...
> + goto cleanup;
> +
> + if (qemuProcessSetSchedParams(iothread_id, thread_id,
> + vm->def->cputune.niothreadsched,
> + vm->def->cputune.iothreadsched) < 0)
> + goto cleanup;
> + }
> + } else {
> +
> + /* Remove our iothread_id from the list of iothreadpid's */
> + if (VIR_DELETE_ELEMENT(priv->iothreadpids, idx,
> + priv->niothreadpids) < 0)
> + goto cleanup;
> +
> + /* Remove the cgroup links */
> + if (qemuDomainDelCgroupForThread(priv->cgroup,
> + VIR_CGROUP_THREAD_IOTHREAD,
> + iothread_id) < 0)
> + goto cleanup;
> +
> + /* Free pin setting */
> + virDomainPinDel(&vm->def->cputune.iothreadspin,
> + &vm->def->cputune.niothreadspin,
> + iothread_id);
I think it would be preferable to split this function into two, one for
adding and one for removing.
> + }
> +
> + ret = 0;
> +
> + cleanup:
> + if (new_iothreads) {
> + for (i = 0; i < new_niothreads; i++)
> + qemuMonitorIOThreadInfoFree(new_iothreads[i]);
> + VIR_FREE(new_iothreads);
> + }
> + VIR_FREE(mem_mask);
> + virDomainAuditIOThread(vm, orig_niothreads, new_niothreads,
> + "update", rc == 0);
> + if (cgroup_iothread)
> + virCgroupFree(&cgroup_iothread);
> + VIR_FREE(alias);
> + return ret;
> +
> + exit_monitor:
> + ignore_value(qemuDomainObjExitMonitor(driver, vm));
> + goto cleanup;
> +}
> +
> +static int
> +qemuDomainChgIOThread(virQEMUDriverPtr driver,
> + virDomainObjPtr vm,
> + unsigned int iothread_id,
> + const char *name,
> + bool add,
> + unsigned int flags)
> +{
> + virQEMUDriverConfigPtr cfg = NULL;
> + virCapsPtr caps = NULL;
> + qemuDomainObjPrivatePtr priv;
> + virCgroupPtr cgroup_temp = NULL;
> + virBitmapPtr all_nodes = NULL;
> + char *all_nodes_str = NULL;
> + char *mem_mask = NULL;
> + virDomainDefPtr persistentDef;
> + size_t i;
> + int ret = -1;
> +
> + if ((unsigned short) iothread_id != iothread_id) {
> + virReportError(VIR_ERR_INVALID_ARG,
> + _("argument out of range: %d"), iothread_id);
Again, please store it as an int and kill this code.
> + return -1;
> + }
> +
> +
> + cfg = virQEMUDriverGetConfig(driver);
> +
> + if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
> + goto cleanup;
> +
> + if (!add) {
> + /* If there is a disk using the IOThread to be removed, then fail. */
> + for (i = 0; i < vm->def->ndisks; i++) {
> + if (vm->def->disks[i]->iothread == iothread_id) {
> + virReportError(VIR_ERR_INVALID_ARG,
> + _("cannot remove IOThread %u since it "
> + "is being used by disk path '%s'"),
> + iothread_id,
> + NULLSTR(vm->def->disks[i]->src->path));
> + goto cleanup;
> + }
> + }
> + }
> +
> + priv = vm->privateData;
> +
> + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
> + goto cleanup;
> +
> + if (flags & VIR_DOMAIN_AFFECT_LIVE) {
This should be after virDomainLiveConfigHelperMethod since it updates
@flags.
> + if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_EMULATOR, 0,
> + false, &cgroup_temp) < 0)
> + goto endjob;
> +
> + if (!(all_nodes = virNumaGetHostNodeset()))
> + goto endjob;
> +
> + if (!(all_nodes_str = virBitmapFormat(all_nodes)))
> + goto endjob;
> +
> + if (virCgroupGetCpusetMems(cgroup_temp, &mem_mask) < 0 ||
> + virCgroupSetCpusetMems(cgroup_temp, all_nodes_str) < 0)
> + goto endjob;
> + }
> +
> + if (virDomainLiveConfigHelperMethod(caps, driver->xmlopt, vm, &flags,
> + &persistentDef) < 0)
> + goto endjob;
> +
> + /* For a live change - let's make sure the binary supports this */
> + if (flags & VIR_DOMAIN_AFFECT_LIVE &&
> + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("IOThreads not supported with this binary"));
> + goto endjob;
The inactive config will fail if qemu doesn't support iothreads, won't
it?
> + }
> +
> + if (flags & VIR_DOMAIN_AFFECT_LIVE) {
> +
> + if (qemuDomainHotplugIOThread(driver, vm, iothread_id, name, add) < 0)
> + goto endjob;
> +
> + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
> + goto endjob;
> + }
> +
> + if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
Having the config section appear before the LIVE section will make it
less prone to return failure but the thread being actually added.
> + virDomainIOThreadIDDefPtr iddef;
> +
> + iddef = virDomainIOThreadIDFind(persistentDef, iothread_id);
> + if (add) {
> + /* Already there? Then error since we cannot have the same
> + * iothread_id listed twice
> + */
> + if (iddef) {
> + virReportError(VIR_ERR_INVALID_ARG,
> + _("an IOThread is already using iothread_id "
> + "'%u' in persistent iothreadids"),
> + iothread_id);
> + goto endjob;
> + }
> +
> + if (virDomainIOThreadIDAdd(persistentDef, iothread_id, name) < 0)
> + goto endjob;
> +
> + /* Nothing to do in iothreadspin list (that's a separate command) */
> +
> + persistentDef->iothreads++;
> + } else {
> + if (!iddef) {
> + virReportError(VIR_ERR_INVALID_ARG,
> + _("cannot find IOThread '%u' in persistent "
> + "iothreadids"),
> + iothread_id);
> + goto cleanup;
> + }
> +
> + virDomainIOThreadIDDel(persistentDef, iothread_id);
> + virDomainPinDel(&persistentDef->cputune.iothreadspin,
> + &persistentDef->cputune.niothreadspin,
> + iothread_id);
> + persistentDef->iothreads--;
> + }
> +
> + if (virDomainSaveConfig(cfg->configDir, persistentDef) < 0)
> + goto endjob;
> + }
> +
> + ret = 0;
> +
> + endjob:
> + if (mem_mask) {
> + virErrorPtr err = virSaveLastError();
> + virCgroupSetCpusetMems(cgroup_temp, mem_mask);
> + virSetError(err);
> + virFreeError(err);
> + }
> + qemuDomainObjEndJob(driver, vm);
> +
> + cleanup:
> + VIR_FREE(mem_mask);
> + VIR_FREE(all_nodes_str);
> + virBitmapFree(all_nodes);
> + virCgroupFree(&cgroup_temp);
> + virObjectUnref(caps);
> + virObjectUnref(cfg);
> + return ret;
> +}
> +
Peter
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20150413/6bc72fb3/attachment-0001.sig>
More information about the libvir-list
mailing list