[libvirt] [PATCH v2 0/4] Introduce display of IOThreads Information

v1: http://www.redhat.com/archives/libvir-list/2015-February/msg00402.html Changes since v1: * Remove 'thread_id' from public display * Add --live, --config, --current flags * Adjust patch 1 to accept/check the flags, modify comments * Adjust patch 3 to handle 'Live' or 'Config' options. Moved most of v1 patch code into the *Live function and add the *Config function to generate the IOThread config data * Adjust patch 4 to allow/check live/config options and set flags John Ferlan (4): Implement public API for virDomainGetIOThreadsInfo remote: Implement the remote plumbing for virDomainGetIOThreads qemu: Implement the qemu driver fetch for IOThreads virsh: Add 'iothreads' command daemon/remote.c | 74 ++++++++++++- include/libvirt/libvirt-domain.h | 21 +++- src/driver-hypervisor.h | 8 +- src/libvirt-domain.c | 75 +++++++++++++- src/libvirt_public.syms | 6 ++ src/qemu/qemu_driver.c | 217 +++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 81 ++++++++++++++- src/remote/remote_protocol.x | 28 ++++- src/remote_protocol-structs | 19 ++++ src/rpc/gendispatch.pl | 1 + tools/virsh-domain.c | 94 +++++++++++++++++ tools/virsh.pod | 12 +++ 12 files changed, 629 insertions(+), 7 deletions(-) -- 2.1.0

Add virDomainGetIOThreadsInfo in order to returned a list of virDomainIOThreadsInfoPtr structures which list the IOThread ID and the CPU Affinity map for each IOThread for domain. For an active domain, the live data will be returned, while for an inactive domain, the config data will be returned. The API supports either the --live or --config flag, but not both. Also added virDomainIOThreadsInfoFree in order to free the cpumap and the array entry structure. Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 21 ++++++++++- src/driver-hypervisor.h | 8 ++++- src/libvirt-domain.c | 75 +++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 6 ++++ 4 files changed, 107 insertions(+), 3 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 4dbd7f5..60423fd 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4,7 +4,7 @@ * Description: Provides APIs for the management of domains * Author: Daniel Veillard <veillard@redhat.com> * - * Copyright (C) 2006-2014 Red Hat, Inc. + * Copyright (C) 2006-2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1568,6 +1568,25 @@ int virDomainGetEmulatorPinInfo (virDomainPtr domain, unsigned int flags); /** + * virIOThreadsInfo: + * + * The data structure for information about IOThreads in a domain + */ +typedef struct _virDomainIOThreadsInfo virDomainIOThreadsInfo; +typedef virDomainIOThreadsInfo *virDomainIOThreadsInfoPtr; +struct _virDomainIOThreadsInfo { + unsigned int iothread_id; /* IOThread ID */ + unsigned char *cpumap; /* CPU map for thread */ + int cpumaplen; /* cpumap size */ +}; + +void virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info); + +int virDomainGetIOThreadsInfo(virDomainPtr domain, + virDomainIOThreadsInfoPtr **info, + unsigned int flags); + +/** * VIR_USE_CPU: * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN/OUT) * @cpu: the physical CPU number diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index a1198ad..2ce1a51 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1,7 +1,7 @@ /* * driver-hypervisor.h: entry points for hypervisor drivers * - * Copyright (C) 2006-2014 Red Hat, Inc. + * Copyright (C) 2006-2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -381,6 +381,11 @@ typedef int (*virDrvDomainGetMaxVcpus)(virDomainPtr domain); typedef int +(*virDrvDomainGetIOThreadsInfo)(virDomainPtr domain, + virDomainIOThreadsInfoPtr **info, + unsigned int flags); + +typedef int (*virDrvDomainGetSecurityLabel)(virDomainPtr domain, virSecurityLabelPtr seclabel); @@ -1254,6 +1259,7 @@ struct _virHypervisorDriver { virDrvDomainGetEmulatorPinInfo domainGetEmulatorPinInfo; virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; + virDrvDomainGetIOThreadsInfo domainGetIOThreadsInfo; virDrvDomainGetSecurityLabel domainGetSecurityLabel; virDrvDomainGetSecurityLabelList domainGetSecurityLabelList; virDrvNodeGetSecurityModel nodeGetSecurityModel; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 492e90a..4a4e392 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -1,7 +1,7 @@ /* * libvirt-domain.c: entry points for virDomainPtr APIs * - * Copyright (C) 2006-2014 Red Hat, Inc. + * Copyright (C) 2006-2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -7891,6 +7891,79 @@ virDomainGetMaxVcpus(virDomainPtr domain) /** + * virDomainGetIOThreadsInfo: + * @dom: a domain object + * @info: pointer to an array of virDomainIOThreadsInfo structures (OUT) + * @flags: bitwise-OR of virDomainModificationImpact + * Must not be VIR_DOMAIN_AFFECT_LIVE and + * VIR_DOMAIN_AFFECT_CONFIG concurrently. + * + * Fetch IOThreads of an active domain including the cpumap information to + * determine on which CPU the IOThread has affinity to run. + * + * Returns the number of IOThreads or -1 in case of error. + * On success, the array of information is stored into @info. The caller is + * responsible for calling virDomainIOThreadsInfoFree() on each array element, + * then calling free() on @info. On error, @info is set to NULL. + */ +int +virDomainGetIOThreadsInfo(virDomainPtr dom, + virDomainIOThreadsInfoPtr **info, + unsigned int flags) +{ + VIR_DOMAIN_DEBUG(dom, "info=%p flags=%x", info, flags); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + virCheckReadOnlyGoto(dom->conn->flags, error); + virCheckNonNullArgGoto(info, error); + *info = NULL; + + /* At most one of these two flags should be set. */ + if ((flags & VIR_DOMAIN_AFFECT_LIVE) && + (flags & VIR_DOMAIN_AFFECT_CONFIG)) { + virReportInvalidArg(flags, + _("flags 'affect live' and 'affect config' in %s " + "are mutually exclusive"), + __FUNCTION__); + goto error; + } + + if (dom->conn->driver->domainGetIOThreadsInfo) { + int ret; + ret = dom->conn->driver->domainGetIOThreadsInfo(dom, info, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + + +/** + * virDomainIOThreadsInfoFree: + * @info: pointer to a virDomainIOThreadsInfo object + * + * Frees the memory used by @info. + */ +void +virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info) +{ + if (!info) + return; + + VIR_FREE(info->cpumap); + VIR_FREE(info); +} + + +/** * virDomainGetSecurityLabel: * @domain: a domain object * @seclabel: pointer to a virSecurityLabel structure diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4ea5cff..d3ddd24 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -695,4 +695,10 @@ LIBVIRT_1.2.12 { virDomainDefineXMLFlags; } LIBVIRT_1.2.11; +LIBVIRT_1.2.13 { + global: + virDomainIOThreadsInfoFree; + virDomainGetIOThreadsInfo; +} LIBVIRT_1.2.12; + # .... define new API here using predicted next version number .... -- 2.1.0

Implement the remote plumbing for virDomainGetIOThreads Signed-off-by: John Ferlan <jferlan@redhat.com> --- daemon/remote.c | 74 +++++++++++++++++++++++++++++++++++++++- src/remote/remote_driver.c | 81 +++++++++++++++++++++++++++++++++++++++++++- src/remote/remote_protocol.x | 28 +++++++++++++-- src/remote_protocol-structs | 19 +++++++++++ src/rpc/gendispatch.pl | 1 + 5 files changed, 199 insertions(+), 4 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index d657a09..f41b1ca 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1,7 +1,7 @@ /* * remote.c: handlers for RPC method calls * - * Copyright (C) 2007-2014 Red Hat, Inc. + * Copyright (C) 2007-2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -2269,6 +2269,78 @@ remoteDispatchDomainGetVcpus(virNetServerPtr server ATTRIBUTE_UNUSED, } static int +remoteDispatchDomainGetIOThreadsInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_iothreads_info_args *args, + remote_domain_get_iothreads_info_ret *ret) +{ + int rv = -1; + size_t i; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + virDomainIOThreadsInfoPtr *info = NULL; + virDomainPtr dom = NULL; + remote_domain_iothreads_info *dst; + int ninfo = 0; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(dom = get_nonnull_domain(priv->conn, args->dom))) + goto cleanup; + + if ((ninfo = virDomainGetIOThreadsInfo(dom, &info, args->flags)) < 0) + goto cleanup; + + if (ninfo > REMOTE_IOTHREADS_INFO_MAX) { + virReportError(VIR_ERR_RPC, + _("Too many IOThreads in info: %d for limit %d"), + ninfo, REMOTE_IOTHREADS_INFO_MAX); + goto cleanup; + } + + if (ninfo) { + if (VIR_ALLOC_N(ret->info.info_val, ninfo) < 0) + goto cleanup; + + ret->info.info_len = ninfo; + + for (i = 0; i < ninfo; i++) { + dst = &ret->info.info_val[i]; + dst->iothread_id = info[i]->iothread_id; + + /* No need to allocate/copy the cpumap if we make the reasonable + * assumption that unsigned char and char are the same size. + */ + dst->cpumap.cpumap_len = info[i]->cpumaplen; + dst->cpumap.cpumap_val = (char *)info[i]->cpumap; + info[i]->cpumap = NULL; + } + } else { + ret->info.info_len = 0; + ret->info.info_val = NULL; + } + + ret->ret = ninfo; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virObjectUnref(dom); + if (ninfo >= 0) + for (i = 0; i < ninfo; i++) + virDomainIOThreadsInfoFree(info[i]); + VIR_FREE(info); + + return rv; +} + +static int remoteDispatchDomainMigratePrepare(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, virNetMessagePtr msg ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d4fd658..944a590 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2,7 +2,7 @@ * remote_driver.c: driver to provide access to libvirtd running * on a remote machine * - * Copyright (C) 2007-2014 Red Hat, Inc. + * Copyright (C) 2007-2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -2316,6 +2316,84 @@ remoteDomainGetVcpus(virDomainPtr domain, } static int +remoteDomainGetIOThreadsInfo(virDomainPtr dom, + virDomainIOThreadsInfoPtr **info, + unsigned int flags) +{ + int rv = -1; + size_t i; + struct private_data *priv = dom->conn->privateData; + remote_domain_get_iothreads_info_args args; + remote_domain_get_iothreads_info_ret ret; + remote_domain_iothreads_info *src; + virDomainIOThreadsInfoPtr *info_ret = NULL; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, dom); + + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + + if (call(dom->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_IOTHREADS_INFO, + (xdrproc_t)xdr_remote_domain_get_iothreads_info_args, + (char *)&args, + (xdrproc_t)xdr_remote_domain_get_iothreads_info_ret, + (char *)&ret) == -1) + goto done; + + if (ret.info.info_len > REMOTE_IOTHREADS_INFO_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Too many IOThreads in info: %d for limit %d"), + ret.info.info_len, REMOTE_IOTHREADS_INFO_MAX); + goto cleanup; + } + + if (info) { + if (!ret.info.info_len) { + *info = NULL; + rv = ret.ret; + goto cleanup; + } + + if (VIR_ALLOC_N(info_ret, ret.info.info_len) < 0) + goto cleanup; + + for (i = 0; i < ret.info.info_len; i++) { + src = &ret.info.info_val[i]; + + if (VIR_ALLOC(info_ret[i]) < 0) + goto cleanup; + + info_ret[i]->iothread_id = src->iothread_id; + if (VIR_ALLOC_N(info_ret[i]->cpumap, src->cpumap.cpumap_len) < 0) + goto cleanup; + memcpy(info_ret[i]->cpumap, src->cpumap.cpumap_val, + src->cpumap.cpumap_len); + info_ret[i]->cpumaplen = src->cpumap.cpumap_len; + } + *info = info_ret; + info_ret = NULL; + } + + rv = ret.ret; + + cleanup: + if (info_ret) { + for (i = 0; i < ret.info.info_len; i++) + virDomainIOThreadsInfoFree(info_ret[i]); + VIR_FREE(info_ret); + } + xdr_free((xdrproc_t)xdr_remote_domain_get_iothreads_info_ret, + (char *) &ret); + + done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteDomainGetSecurityLabel(virDomainPtr domain, virSecurityLabelPtr seclabel) { remote_domain_get_security_label_args args; @@ -8027,6 +8105,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGetEmulatorPinInfo = remoteDomainGetEmulatorPinInfo, /* 0.10.0 */ .domainGetVcpus = remoteDomainGetVcpus, /* 0.3.0 */ .domainGetMaxVcpus = remoteDomainGetMaxVcpus, /* 0.3.0 */ + .domainGetIOThreadsInfo = remoteDomainGetIOThreadsInfo, /* 1.2.13 */ .domainGetSecurityLabel = remoteDomainGetSecurityLabel, /* 0.6.1 */ .domainGetSecurityLabelList = remoteDomainGetSecurityLabelList, /* 0.10.0 */ .nodeGetSecurityModel = remoteNodeGetSecurityModel, /* 0.6.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c8162a5..09bc526 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3,7 +3,7 @@ * remote_internal driver and libvirtd. This protocol is * internal and may change at any time. * - * Copyright (C) 2006-2014 Red Hat, Inc. + * Copyright (C) 2006-2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -85,6 +85,9 @@ const REMOTE_VCPUINFO_MAX = 16384; /* Upper limit on cpumaps (bytes) passed to virDomainGetVcpus. */ const REMOTE_CPUMAPS_MAX = 8388608; +/* Upper limit on number of info fields returned by virDomainGetIOThreads. */ +const REMOTE_IOTHREADS_INFO_MAX = 16384; + /* Upper limit on migrate cookie. */ const REMOTE_MIGRATE_COOKIE_MAX = 4194304; @@ -1181,6 +1184,21 @@ struct remote_domain_get_max_vcpus_ret { int num; }; +struct remote_domain_iothreads_info { + unsigned int iothread_id; + opaque cpumap<REMOTE_CPUMAP_MAX>; +}; + +struct remote_domain_get_iothreads_info_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + +struct remote_domain_get_iothreads_info_ret { + remote_domain_iothreads_info info<REMOTE_IOTHREADS_INFO_MAX>; + unsigned int ret; +}; + struct remote_domain_get_security_label_args { remote_nonnull_domain dom; }; @@ -5569,5 +5587,11 @@ enum remote_procedure { * @acl: domain:write * @acl: domain:save */ - REMOTE_PROC_DOMAIN_DEFINE_XML_FLAGS = 350 + REMOTE_PROC_DOMAIN_DEFINE_XML_FLAGS = 350, + + /** + * @generate: none + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_GET_IOTHREADS_INFO = 351 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index df6eaf3..1d753af 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -807,6 +807,24 @@ struct remote_domain_get_max_vcpus_args { struct remote_domain_get_max_vcpus_ret { int num; }; +struct remote_domain_iothreads_info { + u_int iothread_id; + struct { + u_int cpumap_len; + char * cpumap_val; + } cpumap; +}; +struct remote_domain_get_iothreads_info_args { + remote_nonnull_domain dom; + u_int flags; +}; +struct remote_domain_get_iothreads_info_ret { + struct { + u_int info_len; + remote_domain_iothreads_info * info_val; + } info; + u_int ret; +}; struct remote_domain_get_security_label_args { remote_nonnull_domain dom; }; @@ -2963,4 +2981,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE = 348, REMOTE_PROC_DOMAIN_GET_FSINFO = 349, REMOTE_PROC_DOMAIN_DEFINE_XML_FLAGS = 350, + REMOTE_PROC_DOMAIN_GET_IOTHREADS_INFO = 351, }; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index 0dc167a..78cb415 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -67,6 +67,7 @@ sub fixup_name { $name =~ s/Fsfreeze$/FSFreeze/; $name =~ s/Fsthaw$/FSThaw/; $name =~ s/Fsinfo$/FSInfo/; + $name =~ s/Iothreads$/IOThreads/; $name =~ s/Scsi/SCSI/; $name =~ s/Wwn$/WWN/; $name =~ s/Dhcp$/DHCP/; -- 2.1.0

Depending on the flags passed, either attempt to return the active/live IOThread data for the domain or the config data. The active/live path will call into the Monitor in order to get the IOThread data and then correlate the thread_id's returned from the monitor to the currently running system/threads in order to ascertain the affinity for each iothread_id. The config path will map each of the configured IOThreads and return any configured iothreadspin data Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 709f468..510ab41 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5541,6 +5541,222 @@ qemuDomainGetMaxVcpus(virDomainPtr dom) VIR_DOMAIN_VCPU_MAXIMUM)); } +static int +qemuDomainGetIOThreadsLive(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainIOThreadsInfoPtr **info) +{ + qemuDomainObjPrivatePtr priv; + qemuMonitorIOThreadsInfoPtr *iothreads = NULL; + virDomainIOThreadsInfoPtr *info_ret = NULL; + int niothreads = 0; + int maxcpu, hostcpus, maplen; + size_t i; + int ret = -1; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot list IOThreads for an inactive domain")); + goto endjob; + } + + priv = vm->privateData; + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("IOThreads not supported with this binary")); + goto endjob; + } + + if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + goto endjob; + niothreads = qemuMonitorGetIOThreads(priv->mon, &iothreads); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto endjob; + if (niothreads < 0) + goto endjob; + + /* Nothing to do */ + if (niothreads == 0) { + ret = 0; + goto endjob; + } + + if ((hostcpus = nodeGetCPUCount()) < 0) + goto endjob; + + maplen = VIR_CPU_MAPLEN(hostcpus); + maxcpu = maplen * 8; + if (maxcpu > hostcpus) + maxcpu = hostcpus; + + if (VIR_ALLOC_N(info_ret, niothreads) < 0) + goto endjob; + + for (i = 0; i < niothreads; i++) { + virBitmapPtr map = NULL; + unsigned char *tmpmap = NULL; + int tmpmaplen = 0; + + if (VIR_ALLOC(info_ret[i]) < 0) + goto endjob; + + if (virStrToLong_ui(iothreads[i]->name + strlen("iothread"), NULL, 10, + &info_ret[i]->iothread_id) < 0) + goto endjob; + + if (VIR_ALLOC_N(info_ret[i]->cpumap, maplen) < 0) + goto endjob; + + if (virProcessGetAffinity(iothreads[i]->thread_id, &map, maxcpu) < 0) + goto endjob; + + virBitmapToData(map, &tmpmap, &tmpmaplen); + if (tmpmaplen > maplen) + tmpmaplen = maplen; + memcpy(info_ret[i]->cpumap, tmpmap, tmpmaplen); + info_ret[i]->cpumaplen = tmpmaplen; + + VIR_FREE(tmpmap); + virBitmapFree(map); + } + + *info = info_ret; + info_ret = NULL; + ret = niothreads; + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + if (info_ret) { + for (i = 0; i < niothreads; i++) + virDomainIOThreadsInfoFree(info_ret[i]); + VIR_FREE(info_ret); + } + + return ret; +} + +static int +qemuDomainGetIOThreadsConfig(virDomainDefPtr targetDef, + virDomainIOThreadsInfoPtr **info) +{ + virDomainIOThreadsInfoPtr *info_ret = NULL; + virDomainVcpuPinDefPtr *iothreadspin_list; + virBitmapPtr cpumask = NULL; + unsigned char *cpumap; + int maxcpu, hostcpus, maplen; + size_t i, pcpu; + bool pinned; + int ret = -1; + + if (targetDef->iothreads == 0) + return 0; + + if ((hostcpus = nodeGetCPUCount()) < 0) + goto cleanup; + + maplen = VIR_CPU_MAPLEN(hostcpus); + maxcpu = maplen * 8; + if (maxcpu > hostcpus) + maxcpu = hostcpus; + + if (VIR_ALLOC_N(info_ret, targetDef->iothreads) < 0) + goto cleanup; + + for (i = 0; i < targetDef->iothreads; i++) { + if (VIR_ALLOC(info_ret[i]) < 0) + goto cleanup; + + /* IOThreads being counting at 1 */ + info_ret[i]->iothread_id = i + 1; + + if (VIR_ALLOC_N(info_ret[i]->cpumap, maplen) < 0) + goto cleanup; + + /* Initialize the cpumap */ + info_ret[i]->cpumaplen = maplen; + memset(info_ret[i]->cpumap, 0xff, maplen); + if (maxcpu % 8) + info_ret[i]->cpumap[maplen - 1] &= (1 << maxcpu % 8) - 1; + } + + /* If iothreadspin setting exists, there are unused physical cpus */ + iothreadspin_list = targetDef->cputune.iothreadspin; + for (i = 0; i < targetDef->cputune.niothreadspin; i++) { + /* vcpuid is the iothread_id... + * iothread_id is the index into info_ret + 1, so we can + * assume that the info_ret index we want is vcpuid - 1 + */ + cpumap = info_ret[iothreadspin_list[i]->vcpuid - 1]->cpumap; + cpumask = iothreadspin_list[i]->cpumask; + + for (pcpu = 0; pcpu < maxcpu; pcpu++) { + if (virBitmapGetBit(cpumask, pcpu, &pinned) < 0) + goto cleanup; + if (!pinned) + VIR_UNUSE_CPU(cpumap, pcpu); + } + } + + *info = info_ret; + info_ret = NULL; + ret = targetDef->iothreads; + + cleanup: + if (info_ret) { + for (i = 0; i < targetDef->iothreads; i++) + virDomainIOThreadsInfoFree(info_ret[i]); + VIR_FREE(info_ret); + } + + return ret; +} + +static int +qemuDomainGetIOThreadsInfo(virDomainPtr dom, + virDomainIOThreadsInfoPtr **info, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + virCapsPtr caps = NULL; + virDomainDefPtr targetDef = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainGetIOThreadsInfoEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (!(caps = virQEMUDriverGetCapabilities(driver, false))) + goto cleanup; + + if (virDomainLiveConfigHelperMethod(caps, driver->xmlopt, vm, &flags, + &targetDef) < 0) + goto cleanup; + + /* Coverity didn't realize that targetDef must be set if we got here. */ + sa_assert(targetDef); + + if (flags & VIR_DOMAIN_AFFECT_LIVE) + ret = qemuDomainGetIOThreadsLive(driver, vm, info); + else + ret = qemuDomainGetIOThreadsConfig(targetDef, info); + + cleanup: + qemuDomObjEndAPI(&vm); + virObjectUnref(caps); + return ret; +} + static int qemuDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr seclabel) { virQEMUDriverPtr driver = dom->conn->privateData; @@ -19141,6 +19357,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainGetEmulatorPinInfo = qemuDomainGetEmulatorPinInfo, /* 0.10.0 */ .domainGetVcpus = qemuDomainGetVcpus, /* 0.4.4 */ .domainGetMaxVcpus = qemuDomainGetMaxVcpus, /* 0.4.4 */ + .domainGetIOThreadsInfo = qemuDomainGetIOThreadsInfo, /* 1.2.13 */ .domainGetSecurityLabel = qemuDomainGetSecurityLabel, /* 0.6.1 */ .domainGetSecurityLabelList = qemuDomainGetSecurityLabelList, /* 0.10.0 */ .nodeGetSecurityModel = qemuNodeGetSecurityModel, /* 0.6.1 */ -- 2.1.0

Add the 'iothreads' command to display IOThread Info data. Allow for [--live] or [--config] options in order to display live or config data for an active domain. An active domain may return: $ virsh iothreads $dom IOThread ID CPU Affinity --------------------------------------------- 1 2 2 3 3 0-3 $ echo $? 0 For domains which don't have IOThreads the following is returned: $ virsh iothreads $dom error: No IOThreads found for the domain $ echo $? 0 For domains which are not running the following is returned: $ virsh iothreads $dom --live error: Unable to get domain IOThreads information error: Requested operation is not valid: cannot list IOThreads for an inactive domain $ echo $? 1 Editing a running domains configuration and modifying the iothreadpin data for thread 3 from nothing provided to setting a cpuset of '0-1' and then displaying using --config could display: $ virsh iothreads f18iothr --config IOThread ID CPU Affinity --------------------------------------------- 1 2 2 3 3 0-1 $ Signed-off-by: John Ferlan <jferlan@redhat.com> --- tools/virsh-domain.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 12 +++++++ 2 files changed, 106 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 2506b89..3b4480d 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -6797,6 +6797,94 @@ cmdSetvcpus(vshControl *ctl, const vshCmd *cmd) } /* + * "iothreads" command + */ +static const vshCmdInfo info_iothreads[] = { + {.name = "help", + .data = N_("view domain IOThreads") + }, + {.name = "desc", + .data = N_("Returns basic information about the domain IOThreads.") + }, + {.name = NULL} +}; +static const vshCmdOptDef opts_iothreads[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("domain name, id or uuid") + }, + {.name = "config", + .type = VSH_OT_BOOL, + .help = N_("affect next boot") + }, + {.name = "live", + .type = VSH_OT_BOOL, + .help = N_("affect running domain") + }, + {.name = "current", + .type = VSH_OT_BOOL, + .help = N_("affect current domain") + }, + {.name = NULL} +}; + +static bool +cmdIOThreadsInfo(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool current = vshCommandOptBool(cmd, "current"); + int niothreads = 0; + virDomainIOThreadsInfoPtr *info; + size_t i; + int maxcpu; + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if ((maxcpu = vshNodeGetCPUCount(ctl->conn)) < 0) + goto cleanup; + + if ((niothreads = virDomainGetIOThreadsInfo(dom, &info, flags)) < 0) { + vshError(ctl, _("Unable to get domain IOThreads information")); + goto cleanup; + } + + if (niothreads == 0) { + vshError(ctl, _("No IOThreads found for the domain")); + goto cleanup; + } + + vshPrintExtra(ctl, " %-15s %-15s\n", + _("IOThread ID"), _("CPU Affinity")); + vshPrintExtra(ctl, "---------------------------------------------\n"); + for (i = 0; i < niothreads; i++) { + + vshPrintExtra(ctl, " %-15u ", info[i]->iothread_id); + ignore_value(vshPrintPinInfo(info[i]->cpumap, info[i]->cpumaplen, + maxcpu, 0)); + vshPrint(ctl, "\n"); + virDomainIOThreadsInfoFree(info[i]); + } + VIR_FREE(info); + + cleanup: + virDomainFree(dom); + return niothreads >= 0; +} + +/* * "cpu-compare" command */ static const vshCmdInfo info_cpu_compare[] = { @@ -12697,6 +12785,12 @@ const vshCmdDef domManagementCmds[] = { .info = info_inject_nmi, .flags = 0 }, + {.name = "iothreads", + .handler = cmdIOThreadsInfo, + .opts = opts_iothreads, + .info = info_iothreads, + .flags = 0 + }, {.name = "send-key", .handler = cmdSendKey, .opts = opts_send_key, diff --git a/tools/virsh.pod b/tools/virsh.pod index 50de32c..6d0a6fe 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1360,6 +1360,18 @@ If I<--timeout> is specified, the command gives up waiting for events after I<seconds> have elapsed. With I<--loop>, the command prints all events until a timeout or interrupt key. +=item B<iothreads> I<domain> [[I<--live>] [I<--config>] | [I<--current>]] + +Display basic domain IOThreads information including the IOThread ID and +the CPU Affinity for each IOThread. + +If I<--live> is specified, get the IOThreads data from the running guest. If +the guest is not running, an error is returned. +If I<--config> is specified, get the IOThreads data from the next boot of +a persistent guest. +If I<--current> is specified or I<--live> and I<--config> are not specified, +then get the IOThread data based on the current guest state. + =item B<managedsave> I<domain> [I<--bypass-cache>] [{I<--running> | I<--paused>}] [I<--verbose>] -- 2.1.0

On 02/16/2015 04:21 PM, John Ferlan wrote:
v1: http://www.redhat.com/archives/libvir-list/2015-February/msg00402.html
Changes since v1: * Remove 'thread_id' from public display * Add --live, --config, --current flags * Adjust patch 1 to accept/check the flags, modify comments * Adjust patch 3 to handle 'Live' or 'Config' options. Moved most of v1 patch code into the *Live function and add the *Config function to generate the IOThread config data * Adjust patch 4 to allow/check live/config options and set flags
John Ferlan (4): Implement public API for virDomainGetIOThreadsInfo remote: Implement the remote plumbing for virDomainGetIOThreads qemu: Implement the qemu driver fetch for IOThreads virsh: Add 'iothreads' command
daemon/remote.c | 74 ++++++++++++- include/libvirt/libvirt-domain.h | 21 +++- src/driver-hypervisor.h | 8 +- src/libvirt-domain.c | 75 +++++++++++++- src/libvirt_public.syms | 6 ++ src/qemu/qemu_driver.c | 217 +++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 81 ++++++++++++++- src/remote/remote_protocol.x | 28 ++++- src/remote_protocol-structs | 19 ++++ src/rpc/gendispatch.pl | 1 + tools/virsh-domain.c | 94 +++++++++++++++++ tools/virsh.pod | 12 +++ 12 files changed, 629 insertions(+), 7 deletions(-)
Self NACK - forgot to add the resources as described in the bz I'm working on... New series posted shortly <sigh> While I'm at it I'll post the SetIOThreads logic to fulfill the bz... John
participants (1)
-
John Ferlan