[libvirt] [PATCH v3 0/9] Introduce display of IOThreads Information

v2 here: http://www.redhat.com/archives/libvir-list/2015-February/msg00562.html Changes over v2: Patches 1-4 * Add "resources" and "nresources" in order to return the resources that are using an IOThread * Fixed memory leak - neglected to free the qemuMonitorIOThreadsInfoPtr 'iothreads' in patch 3 Patches 5-9 (NEW) * Patches 5 & 6 implement the API/remote protocol for virDomainSetIOThreads * Patch 7 implements a couple of helper function - copies of existing vcpupin functions * Patch 8 implements the qemu backend qemuDomainSetIOThreads in order to allow setting IOThread pin data either for live or config. Heavily lifted from existing vcpu code * Patch 9 implements the virsh command options in order to set the pinning on a specific thread. As a side effect/benefit one can now display just one IOThread if so desired. All the changes are sufficient to satisfy the following bz: https://bugzilla.redhat.com/show_bug.cgi?id=1135491 John Ferlan (9): Implement public API for virDomainGetIOThreadsInfo remote: Implement the remote plumbing for virDomainGetIOThreadsInfo qemu: Implement the qemu driver fetch for IOThreads virsh: Add 'iothreads' command Implement public API for virDomainSetIOThreads remote: Implement remote plumbing for virDomainSetIOThreads domain: Introduce virDomainIOThreadsPin{Add|Del} qemu: Add qemuDomainSetIOThreads virsh: Allow setting of the iothread pin daemon/remote.c | 99 +++++++- include/libvirt/libvirt-domain.h | 48 +++- src/conf/domain_conf.c | 64 +++++ src/conf/domain_conf.h | 10 + src/driver-hypervisor.h | 16 +- src/libvirt-domain.c | 149 ++++++++++- src/libvirt_private.syms | 2 + src/libvirt_public.syms | 7 + src/qemu/qemu_driver.c | 519 +++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 94 ++++++- src/remote/remote_protocol.x | 48 +++- src/remote_protocol-structs | 33 +++ src/rpc/gendispatch.pl | 1 + tools/virsh-domain.c | 154 ++++++++++++ tools/virsh.pod | 20 ++ 15 files changed, 1257 insertions(+), 7 deletions(-) -- 2.1.0

Add virDomainGetIOThreadsInfo in order to return a list of virDomainIOThreadsInfoPtr structures which list the IOThread ID, the CPU Affinity map, and associated resources for each IOThread for the domain. For an active domain, the live data will be returned, while for an inactive domain, the config data will be returned. The resources data is expected to be the src path for the disks that have an assigned iothread. The API supports either the --live or --config flag, but not both. Also added virDomainIOThreadsInfoFree in order to free the cpumap, list of resources, and the array entry structure. Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 23 +++++++++++- src/driver-hypervisor.h | 8 +++- src/libvirt-domain.c | 80 +++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 6 +++ 4 files changed, 114 insertions(+), 3 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 4dbd7f5..9dcc424 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,27 @@ 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 */ + size_t nresources; /* count of resources using IOThread */ + char **resources; /* array of resources using IOThread */ +}; + +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..f893b35 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,84 @@ 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) +{ + size_t i; + + if (!info) + return; + + VIR_FREE(info->cpumap); + for (i = 0; i < info->nresources; i++) + VIR_FREE(info->resources[i]); + VIR_FREE(info->resources); + 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

On 02/17/2015 04:03 PM, John Ferlan wrote:
Add virDomainGetIOThreadsInfo in order to return a list of virDomainIOThreadsInfoPtr structures which list the IOThread ID, the CPU Affinity map, and associated resources for each IOThread for the domain. For an active domain, the live data will be returned, while for an inactive domain, the config data will be returned. The resources data is expected to be the src path for the disks that have an assigned iothread.
The API supports either the --live or --config flag, but not both.
Also added virDomainIOThreadsInfoFree in order to free the cpumap, list of resources, and the array entry structure.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 23 +++++++++++- src/driver-hypervisor.h | 8 +++- src/libvirt-domain.c | 80 +++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 6 +++ 4 files changed, 114 insertions(+), 3 deletions(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 4dbd7f5..9dcc424 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,27 @@ 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 */ + size_t nresources; /* count of resources using IOThread */ + char **resources; /* array of resources using IOThread */ +};
As an outsider, I'm left a bit uninformed about exactly what will be in cpumap and resources. Is this information available elsewhere in the documentation already?
+ +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..f893b35 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,84 @@ 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) +{ + size_t i; + + if (!info) + return; + + VIR_FREE(info->cpumap); + for (i = 0; i < info->nresources; i++) + VIR_FREE(info->resources[i]); + VIR_FREE(info->resources); + 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 {
Of course this will now have to change.
+ global: + virDomainIOThreadsInfoFree; + virDomainGetIOThreadsInfo; +} LIBVIRT_1.2.12; + # .... define new API here using predicted next version number ....
I haven't added any new API in a long time, but everything I see here seems in order, as long as there are no other comments from the peanut gallery about exactly what should be in the info (I see you've removed the pid as suggested by danpb). So ACK assuming that better documentation on the fields of the info is already available elsewhere. (Anybody else feel free to jump in and point out anything missing from the "new API checklist" that I didn't see).

On 02/24/2015 03:11 PM, Laine Stump wrote:
On 02/17/2015 04:03 PM, John Ferlan wrote:
Add virDomainGetIOThreadsInfo in order to return a list of virDomainIOThreadsInfoPtr structures which list the IOThread ID, the CPU Affinity map, and associated resources for each IOThread for the domain. For an active domain, the live data will be returned, while for an inactive domain, the config data will be returned. The resources data is expected to be the src path for the disks that have an assigned iothread.
The API supports either the --live or --config flag, but not both.
Also added virDomainIOThreadsInfoFree in order to free the cpumap, list of resources, and the array entry structure.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 23 +++++++++++- src/driver-hypervisor.h | 8 +++- src/libvirt-domain.c | 80 +++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 6 +++ 4 files changed, 114 insertions(+), 3 deletions(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 4dbd7f5..9dcc424 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,27 @@ 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 */ + size_t nresources; /* count of resources using IOThread */ + char **resources; /* array of resources using IOThread */ +};
As an outsider, I'm left a bit uninformed about exactly what will be in cpumap and resources. Is this information available elsewhere in the documentation already?
Point well taken... Just a little lower in the file cpumap is explained a few times, but I can copy those descriptions here too John

On Tue, Feb 17, 2015 at 04:03:50PM -0500, John Ferlan wrote:
Add virDomainGetIOThreadsInfo in order to return a list of virDomainIOThreadsInfoPtr structures which list the IOThread ID, the CPU Affinity map, and associated resources for each IOThread for the domain. For an active domain, the live data will be returned, while for an inactive domain, the config data will be returned. The resources data is expected to be the src path for the disks that have an assigned iothread.
The API supports either the --live or --config flag, but not both.
Also added virDomainIOThreadsInfoFree in order to free the cpumap, list of resources, and the array entry structure.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 23 +++++++++++- src/driver-hypervisor.h | 8 +++- src/libvirt-domain.c | 80 +++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 6 +++ 4 files changed, 114 insertions(+), 3 deletions(-)
/** + * virIOThreadsInfo:
s/Threads/Thread/ as it only contains info about one thread.
+ * + * 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 */
Since iothread ids are sequential, starting from 1, this value will always be the index in the array + 1.
+ unsigned char *cpumap; /* CPU map for thread */ + int cpumaplen; /* cpumap size */
+ size_t nresources; /* count of resources using IOThread */ + char **resources; /* array of resources using IOThread */
"resources" is too vague. Moreover, storing the source path here does not seem that much useful to me, considering that the corresponding *Set API cannot change the resources used by the thread (also, some disks don't even have a path). Considering that the XML format is copying <vcpupin>, maybe this API could have a similar signature? I.e.: int virDomainGetIOThreadPin(virDomainPtr domain, unsigned int ncpumaps, unsigned char *cpumaps, unsigned int maplen, unsigned int flags) Or without the 'ncpumaps' parameter, so we don't need an API to get the number of iothreads. Another possibility is getting rid of the 'maplen' as well and return the bitmap as formatted by virBitmapFormat (but the client app still needs the node CPU count to interpret it correctly). Jan

On 03/04/2015 12:24 PM, Ján Tomko wrote:
On Tue, Feb 17, 2015 at 04:03:50PM -0500, John Ferlan wrote:
Add virDomainGetIOThreadsInfo in order to return a list of virDomainIOThreadsInfoPtr structures which list the IOThread ID, the CPU Affinity map, and associated resources for each IOThread for the domain. For an active domain, the live data will be returned, while for an inactive domain, the config data will be returned. The resources data is expected to be the src path for the disks that have an assigned iothread.
The API supports either the --live or --config flag, but not both.
Also added virDomainIOThreadsInfoFree in order to free the cpumap, list of resources, and the array entry structure.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 23 +++++++++++- src/driver-hypervisor.h | 8 +++- src/libvirt-domain.c | 80 +++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 6 +++ 4 files changed, 114 insertions(+), 3 deletions(-)
/** + * virIOThreadsInfo:
s/Threads/Thread/ as it only contains info about one thread.
Since IOThreads was the "techology name" - I just followed suit and used it, but will change it.
+ * + * 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 */
Since iothread ids are sequential, starting from 1, this value will always be the index in the array + 1.
Well - today they are, but once the "Add" and "Remove" functionality is added we could have gaps since you'll be able to define/start with 3 threads and conceivably delete thread #2 (or the middle one).
+ unsigned char *cpumap; /* CPU map for thread */ + int cpumaplen; /* cpumap size */
+ size_t nresources; /* count of resources using IOThread */ + char **resources; /* array of resources using IOThread */
"resources" is too vague.
Suggestion? Is "devices" better? Today it's the disk source path, but I remember reading something where an IOThread could be potentially used for something else (perhaps network, but I cannot find the reference quickly).
Moreover, storing the source path here does not seem that much useful to me, considering that the corresponding *Set API cannot change the resources used by the thread (also, some disks don't even have a path).
Adding a disk to a domain and assigning an iothread to it is accomplished via the 'attach-disk'. There's a "finite" set of support for now - a virtio-scsi local disk, which I thought had to have a path defined as opposed to some remote disk where the "path" is generated.
Considering that the XML format is copying <vcpupin>, maybe this API could have a similar signature? I.e.:
int virDomainGetIOThreadPin(virDomainPtr domain, unsigned int ncpumaps, unsigned char *cpumaps, unsigned int maplen, unsigned int flags)
Or without the 'ncpumaps' parameter, so we don't need an API to get the number of iothreads.
Another possibility is getting rid of the 'maplen' as well and return the bitmap as formatted by virBitmapFormat (but the client app still needs the node CPU count to interpret it correctly).
This is where I disagree. The "GetIOThreadsInfo" API is more like the vcpuinfo API insomuch as it gets multiple aspects of IOThread objects. As a way of understanding my thought process - I went with the one API rather than an API to get IOThread data and a separate API to get IOThread pin data because I felt a one round trip operation was 'better' than 1..n round trips. If a separate API is requested or required, I'll add it, but I'd prefer to not call it during the IOThreadsInfo fetch operation. Thanks for the review - John

On Thu, Mar 05, 2015 at 07:45:51AM -0500, John Ferlan wrote:
+ unsigned char *cpumap; /* CPU map for thread */ + int cpumaplen; /* cpumap size */
+ size_t nresources; /* count of resources using IOThread */ + char **resources; /* array of resources using IOThread */
"resources" is too vague.
Suggestion? Is "devices" better?
Today it's the disk source path, but I remember reading something where an IOThread could be potentially used for something else (perhaps network, but I cannot find the reference quickly).
I just worry that putting the path here could be a problem sometime in the future if the attribute gets extended (I think some of the Block* APIs had that problem). Perhaps Peter or Eric will voice their opinion. Jan

On Thu, Mar 05, 2015 at 05:21:41PM +0100, Ján Tomko wrote:
On Thu, Mar 05, 2015 at 07:45:51AM -0500, John Ferlan wrote:
+ unsigned char *cpumap; /* CPU map for thread */ + int cpumaplen; /* cpumap size */
+ size_t nresources; /* count of resources using IOThread */ + char **resources; /* array of resources using IOThread */
"resources" is too vague.
Suggestion? Is "devices" better?
Today it's the disk source path, but I remember reading something where an IOThread could be potentially used for something else (perhaps network, but I cannot find the reference quickly).
I just worry that putting the path here could be a problem sometime in the future if the attribute gets extended (I think some of the Block* APIs had that problem).
Perhaps Peter or Eric will voice their opinion.
The XML config already provides information about what device is associated with what I/O thread. As such I don't think we should include the 'resources' field in the struct here at all. It is just duplicating information from the XML, in a format that is not at all extensible, which is just asking for trouble as you point out. These proposed APIs are all about reporting & updating the CPU pinning of the I/O threads, and info about the list of resource names is not relevant for that task, so again this just seems like extra pain for no gain. IOW, just drop the 'resources' and 'nresources' fields from the struct Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

Implement the remote plumbing for virDomainGetIOThreadsInfo Signed-off-by: John Ferlan <jferlan@redhat.com> --- daemon/remote.c | 99 +++++++++++++++++++++++++++++++++++++++++++- src/remote/remote_driver.c | 93 ++++++++++++++++++++++++++++++++++++++++- src/remote/remote_protocol.x | 32 +++++++++++++- src/remote_protocol-structs | 23 ++++++++++ src/rpc/gendispatch.pl | 1 + 5 files changed, 244 insertions(+), 4 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index d657a09..29fe794 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,103 @@ 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, j; + 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; + + if (info[i]->nresources > REMOTE_DOMAIN_IOTHREADS_RESOURCES_MAX) { + virReportError(VIR_ERR_RPC, + _("Too many resources in iothreads: %zd for " + "limit %d"), + info[i]->nresources, + REMOTE_DOMAIN_IOTHREADS_RESOURCES_MAX); + goto cleanup; + } + + if (info[i]->nresources > 0) { + if (VIR_ALLOC_N(dst->resources.resources_val, + info[i]->nresources) < 0) + goto cleanup; + + for (j = 0; j < info[i]->nresources; j++) { + if (VIR_STRDUP(dst->resources.resources_val[j], + info[i]->resources[j]) < 0) + goto cleanup; + } + dst->resources.resources_len = info[i]->nresources; + } else { + dst->resources.resources_val = NULL; + dst->resources.resources_len = 0; + } + } + } 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..e7fa843 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,96 @@ remoteDomainGetVcpus(virDomainPtr domain, } static int +remoteDomainGetIOThreadsInfo(virDomainPtr dom, + virDomainIOThreadsInfoPtr **info, + unsigned int flags) +{ + int rv = -1; + size_t i, j; + 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_ret[i]->nresources = src->resources.resources_len; + if (src->resources.resources_len && + VIR_ALLOC_N(info_ret[i]->resources, + src->resources.resources_len) < 0) + goto cleanup; + + for (j = 0; j < src->resources.resources_len; j++) { + if (VIR_STRDUP(info_ret[i]->resources[j], + src->resources.resources_val[j]) < 0) + goto cleanup; + } + } + *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 +8117,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..63fe547 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,12 @@ 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 number of disks per mountpoint in fsinfo */ +const REMOTE_DOMAIN_IOTHREADS_RESOURCES_MAX = 128; + /* Upper limit on migrate cookie. */ const REMOTE_MIGRATE_COOKIE_MAX = 4194304; @@ -1181,6 +1187,22 @@ struct remote_domain_get_max_vcpus_ret { int num; }; +struct remote_domain_iothreads_info { + unsigned int iothread_id; + opaque cpumap<REMOTE_CPUMAP_MAX>; + remote_nonnull_string resources<REMOTE_DOMAIN_IOTHREADS_RESOURCES_MAX>; /* (const char **) */ +}; + +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 +5591,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..e6cb5b9 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -807,6 +807,28 @@ 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 { + u_int resources_len; + remote_nonnull_string * resources_val; + } resources; +}; +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 +2985,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 Both paths will peruse the 'targetDef' domain list looking for 'disks' that have been assigned to a specific IOThread. An IOThread may have no resources associated Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1bbbe9b..2c9d08c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5541,6 +5541,286 @@ qemuDomainGetMaxVcpus(virDomainPtr dom) VIR_DOMAIN_VCPU_MAXIMUM)); } +static int +qemuDomainGetIOThreadsResources(virDomainDefPtr targetDef, + int niothreads, + virDomainIOThreadsInfoPtr **info) +{ + virDomainIOThreadsInfoPtr *iothrp = *info; + size_t i, j; + int ret = -1; + + for (i = 0; i < targetDef->ndisks; i++) { + virDomainDiskDefPtr disk = targetDef->disks[i]; + + if (!disk->iothread) + continue; + + /* Find the info entry for this thread_id */ + for (j = 0; j < niothreads; j++) { + if (disk->iothread == iothrp[j]->iothread_id) + break; + } + /* Shouldn't happen, but let's not take any chances */ + if (j == niothreads) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("disk source '%s' assigned thread_id '%u' " + "which doesn't exist"), + disk->src->path, disk->iothread); + goto cleanup; + } + + if (VIR_EXPAND_N(iothrp[j]->resources, iothrp[j]->nresources, 1) < 0) + goto cleanup; + + if (VIR_STRDUP(iothrp[j]->resources[iothrp[j]->nresources - 1], + disk->src->path) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret < 0) { + for (i = 0; i < niothreads; i++) + virDomainIOThreadsInfoFree(*info[i]); + VIR_FREE(*info); + } + return ret; +} + +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); + } + if (iothreads) { + for (i = 0; i < niothreads; i++) + qemuMonitorIOThreadsInfoFree(iothreads[i]); + VIR_FREE(iothreads); + } + + 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; + + if (flags & VIR_DOMAIN_AFFECT_LIVE) + targetDef = vm->def; + + /* 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); + + /* If we have IOThreads, then associate the resources using IOThreads + * If this fails, then info is deleted and we return -1 + */ + if (ret > 0) { + if (qemuDomainGetIOThreadsResources(targetDef, ret, info) < 0) + ret = -1; + } + + cleanup: + qemuDomObjEndAPI(&vm); + virObjectUnref(caps); + return ret; +} + static int qemuDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr seclabel) { virQEMUDriverPtr driver = dom->conn->privateData; @@ -19141,6 +19421,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

On Tue, Feb 17, 2015 at 04:03:52PM -0500, John Ferlan wrote:
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
Both paths will peruse the 'targetDef' domain list looking for 'disks' that have been assigned to a specific IOThread. An IOThread may have no resources associated
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+)
Just a few nits, until the API gets final...
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1bbbe9b..2c9d08c 100644
+static int + + 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;
EnterMonitorAsync with ASYNC_JOB_NONE is essentially EnterMonitor
+ 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;
virBitmapToData could make this more readable.
+ } + + /* If iothreadspin setting exists, there are unused physical cpus */ + iothreadspin_list = targetDef->cputune.iothreadspin;
A temporary variable pointing to iothreadspin[i] is more common.
+ 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); + } + } +
Jan

On 03/04/2015 12:45 PM, Ján Tomko wrote:
On Tue, Feb 17, 2015 at 04:03:52PM -0500, John Ferlan wrote:
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
Both paths will peruse the 'targetDef' domain list looking for 'disks' that have been assigned to a specific IOThread. An IOThread may have no resources associated
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_driver.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+)
Just a few nits, until the API gets final...
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1bbbe9b..2c9d08c 100644
+static int + + 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;
EnterMonitorAsync with ASYNC_JOB_NONE is essentially EnterMonitor
+ 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;
virBitmapToData could make this more readable.
This is where I'm not sure varying from the existing API's model w/r/t cpumap & maplen, etc. should be done..
+ } + + /* If iothreadspin setting exists, there are unused physical cpus */ + iothreadspin_list = targetDef->cputune.iothreadspin;
A temporary variable pointing to iothreadspin[i] is more common.
straight copy of qemuDomainGetVcpuPinInfo
+ 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); + } + } +
tks - John

On Thu, Mar 05, 2015 at 07:51:33AM -0500, John Ferlan wrote:
+ 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;
virBitmapToData could make this more readable.
This is where I'm not sure varying from the existing API's model w/r/t cpumap & maplen, etc. should be done..
Yes, virBitmapToData converts it from the virBitmap format used by theiothreadspin info to the cpumap/maplen format matching the API you proposed.
+ } + + /* If iothreadspin setting exists, there are unused physical cpus */ + iothreadspin_list = targetDef->cputune.iothreadspin;
A temporary variable pointing to iothreadspin[i] is more common.
straight copy of qemuDomainGetVcpuPinInfo
Oh, that explains it. Jan

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 Resource(s) ----------------------------------------------------------------- 1 2 /home/vm-images/f18 2 3 /home/vm-images/iothr-vol1 3 0 $ 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: domain is not running $ echo $? 1 Editing a 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 would display: $ virsh iothreads f18iothr --config IOThread ID CPU Affinity Resource(s) --------------------------------------------- 1 2 /home/vm-images/f18 2 3 /home/vm-images/iothr-vol1 3 0-1 $ Editing a domains configuration to assign both Resources to IOThread ID 1 and then displaying again results in: $ virsh iothreads f18iothr --config IOThread ID CPU Affinity Resource(s) ----------------------------------------------------------------- 1 2 /home/vm-images/f18 /home/vm-images/iothr-vol1 2 3 3 0-1 Signed-off-by: John Ferlan <jferlan@redhat.com> --- tools/virsh-domain.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 12 ++++++ 2 files changed, 113 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 2506b89..ffb0392 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -6797,6 +6797,101 @@ 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, j; + 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 %-15s\n", + _("IOThread ID"), _("CPU Affinity"), _("Resource(s)")); + 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)); + for (j = 0; j < info[i]->nresources; j++) { + if (j == 0) + vshPrintExtra(ctl, "\t\t %s", info[i]->resources[j]); + else + vshPrintExtra(ctl, "\n %-15s %s\t\t %s", + " ", " ", info[i]->resources[j]); + } + 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 +12792,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..22e988b 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, +the CPU Affinity, and Resources assigned to 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 Tue, Feb 17, 2015 at 04:03:53PM -0500, John Ferlan wrote:
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 Resource(s) ----------------------------------------------------------------- 1 2 /home/vm-images/f18 2 3 /home/vm-images/iothr-vol1 3 0
$ 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
Printing an error, but not returning one seems strange. Do we do this somewhere else too?
+ + vshPrintExtra(ctl, " %-15s %-15s %-15s\n", + _("IOThread ID"), _("CPU Affinity"), _("Resource(s)")); + vshPrintExtra(ctl, "-----------------------------------------------------------------\n"); + for (i = 0; i < niothreads; i++) { + + vshPrintExtra(ctl, " %-15u ", info[i]->iothread_id);
vshPrint should be used for the actual data, to make the command work with --quiet.
+ ignore_value(vshPrintPinInfo(info[i]->cpumap, info[i]->cpumaplen, + maxcpu, 0)); + for (j = 0; j < info[i]->nresources; j++) { + if (j == 0) + vshPrintExtra(ctl, "\t\t %s", info[i]->resources[j]); + else + vshPrintExtra(ctl, "\n %-15s %s\t\t %s", + " ", " ", info[i]->resources[j]); + }
Jan

On 03/04/2015 12:48 PM, Ján Tomko wrote:
On Tue, Feb 17, 2015 at 04:03:53PM -0500, John Ferlan wrote:
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 Resource(s) ----------------------------------------------------------------- 1 2 /home/vm-images/f18 2 3 /home/vm-images/iothr-vol1 3 0
$ 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
Printing an error, but not returning one seems strange. Do we do this somewhere else too?
Hmm.. Cannot remember my reference on this... Searched on "No " in virsh-domain.c and found "No current block job".. I could change to using vshPrintExtra for the same text if that seems better.
+ + vshPrintExtra(ctl, " %-15s %-15s %-15s\n", + _("IOThread ID"), _("CPU Affinity"), _("Resource(s)")); + vshPrintExtra(ctl, "-----------------------------------------------------------------\n"); + for (i = 0; i < niothreads; i++) { + + vshPrintExtra(ctl, " %-15u ", info[i]->iothread_id);
vshPrint should be used for the actual data, to make the command work with --quiet.
Over aggressive with copy-n-paste... tks, John
+ ignore_value(vshPrintPinInfo(info[i]->cpumap, info[i]->cpumaplen, + maxcpu, 0)); + for (j = 0; j < info[i]->nresources; j++) { + if (j == 0) + vshPrintExtra(ctl, "\t\t %s", info[i]->resources[j]); + else + vshPrintExtra(ctl, "\n %-15s %s\t\t %s", + " ", " ", info[i]->resources[j]); + }
Jan

https://bugzilla.redhat.com/show_bug.cgi?id=1135491 Add the libvirt API infrastructure to support setting IOThread data. For now this is the pinned CPU information, but eventually will also support hot plug add/del The API will support the LIVE, CONFIG, or CURRENT flags. If the VIR_DOMAIN_IOTHREADS_PIN flag is not provided, it's left up to the hypervisor to handle, but when not provided the cpumap/maplen arguments are not checked. Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 16 ++++++++++ src/driver-hypervisor.h | 8 +++++ src/libvirt-domain.c | 69 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 94 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 9dcc424..e07db16 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -1582,11 +1582,27 @@ struct _virDomainIOThreadsInfo { char **resources; /* array of resources using IOThread */ }; +/* Flags for controlling virtual IOThread pinning. */ +typedef enum { + /* See virDomainModificationImpact for these flags. */ + VIR_DOMAIN_IOTHREADS_CURRENT = VIR_DOMAIN_AFFECT_CURRENT, + VIR_DOMAIN_IOTHREADS_LIVE = VIR_DOMAIN_AFFECT_LIVE, + VIR_DOMAIN_IOTHREADS_CONFIG = VIR_DOMAIN_AFFECT_CONFIG, + + /* Additionally, these flags may be bitwise-OR'd in. */ + VIR_DOMAIN_IOTHREADS_PIN = (1 << 2), /* thread_id to pin using cpumap */ +} virDomainIOThreadsFlags; + void virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info); int virDomainGetIOThreadsInfo(virDomainPtr domain, virDomainIOThreadsInfoPtr **info, unsigned int flags); +int virDomainSetIOThreads(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags); /** * VIR_USE_CPU: diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 2ce1a51..120d761 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -386,6 +386,13 @@ typedef int unsigned int flags); typedef int +(*virDrvDomainSetIOThreads)(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags); + +typedef int (*virDrvDomainGetSecurityLabel)(virDomainPtr domain, virSecurityLabelPtr seclabel); @@ -1260,6 +1267,7 @@ struct _virHypervisorDriver { virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; virDrvDomainGetIOThreadsInfo domainGetIOThreadsInfo; + virDrvDomainSetIOThreads domainSetIOThreads; virDrvDomainGetSecurityLabel domainGetSecurityLabel; virDrvDomainGetSecurityLabelList domainGetSecurityLabelList; virDrvNodeGetSecurityModel nodeGetSecurityModel; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index f893b35..bf73773 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -7969,6 +7969,75 @@ virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info) /** + * virDomainSetIOThreads: + * @domain: a domain object + * @iothread_val: either the thread_id to modify or a count of IOThreads + * to be added or removed from the domain depending on the @flags setting + * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN) + * Each bit set to 1 means that corresponding CPU is usable. + * Bytes are stored in little-endian order: CPU0-7, 8-15... + * In each byte, lowest CPU number is least significant bit. + * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in + * underlying virtualization system (Xen...). + * If maplen < size, missing bytes are set to zero. + * If maplen > size, failure code is returned. + * @flags: bitwise-OR of supported virDomainIOThreadsFlags + * + * If the VIR_DOMAIN_IOTHREADS_PIN flag is set, the @iothread_val will be + * an existing IOThread to be pinned + * + * Returns 0 in case of success, -1 in case of failure. + */ +int +virDomainSetIOThreads(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "iothread_val=%u, cpumap=%p, maplen=%d", + iothread_val, cpumap, maplen); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + conn = domain->conn; + + virCheckReadOnlyGoto(conn->flags, error); + if ((unsigned short) iothread_val != iothread_val) { + virReportError(VIR_ERR_OVERFLOW, _("input too large: %u"), + iothread_val); + goto error; + } + + virCheckPositiveArgGoto(iothread_val, error); + + /* If we're pinning threads, then need to ensure cpumap/maplen are valid */ + if (flags & VIR_DOMAIN_IOTHREADS_PIN) { + virCheckNonNullArgGoto(cpumap, error); + virCheckPositiveArgGoto(maplen, error); + } + + if (conn->driver->domainSetIOThreads) { + int ret; + ret = conn->driver->domainSetIOThreads(domain, iothread_val, + cpumap, maplen, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(domain->conn); + return -1; +} + + +/** * virDomainGetSecurityLabel: * @domain: a domain object * @seclabel: pointer to a virSecurityLabel structure diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index d3ddd24..b1a9e83 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -699,6 +699,7 @@ LIBVIRT_1.2.13 { global: virDomainIOThreadsInfoFree; virDomainGetIOThreadsInfo; + virDomainSetIOThreads; } LIBVIRT_1.2.12; # .... define new API here using predicted next version number .... -- 2.1.0

On Tue, Feb 17, 2015 at 04:03:54PM -0500, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1135491
Add the libvirt API infrastructure to support setting IOThread data. For now this is the pinned CPU information, but eventually will also support hot plug add/del
The API will support the LIVE, CONFIG, or CURRENT flags. If the VIR_DOMAIN_IOTHREADS_PIN flag is not provided, it's left up to the hypervisor to handle, but when not provided the cpumap/maplen arguments are not checked.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 16 ++++++++++ src/driver-hypervisor.h | 8 +++++ src/libvirt-domain.c | 69 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 94 insertions(+)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 9dcc424..e07db16 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -1582,11 +1582,27 @@ struct _virDomainIOThreadsInfo { char **resources; /* array of resources using IOThread */ };
+/* Flags for controlling virtual IOThread pinning. */ +typedef enum { + /* See virDomainModificationImpact for these flags. */ + VIR_DOMAIN_IOTHREADS_CURRENT = VIR_DOMAIN_AFFECT_CURRENT, + VIR_DOMAIN_IOTHREADS_LIVE = VIR_DOMAIN_AFFECT_LIVE, + VIR_DOMAIN_IOTHREADS_CONFIG = VIR_DOMAIN_AFFECT_CONFIG, + + /* Additionally, these flags may be bitwise-OR'd in. */ + VIR_DOMAIN_IOTHREADS_PIN = (1 << 2), /* thread_id to pin using cpumap */ +} virDomainIOThreadsFlags; + void virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info);
int virDomainGetIOThreadsInfo(virDomainPtr domain, virDomainIOThreadsInfoPtr **info, unsigned int flags); +int virDomainSetIOThreads(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags);
/** * VIR_USE_CPU: diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 2ce1a51..120d761 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -386,6 +386,13 @@ typedef int unsigned int flags);
typedef int +(*virDrvDomainSetIOThreads)(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags); + +typedef int (*virDrvDomainGetSecurityLabel)(virDomainPtr domain, virSecurityLabelPtr seclabel);
@@ -1260,6 +1267,7 @@ struct _virHypervisorDriver { virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; virDrvDomainGetIOThreadsInfo domainGetIOThreadsInfo; + virDrvDomainSetIOThreads domainSetIOThreads; virDrvDomainGetSecurityLabel domainGetSecurityLabel; virDrvDomainGetSecurityLabelList domainGetSecurityLabelList; virDrvNodeGetSecurityModel nodeGetSecurityModel; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index f893b35..bf73773 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -7969,6 +7969,75 @@ virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info)
/** + * virDomainSetIOThreads: + * @domain: a domain object + * @iothread_val: either the thread_id to modify or a count of IOThreads + * to be added or removed from the domain depending on the @flags setting
IMO this would look nicer as two separate APIs: virDomainSetIOThreadPin virDomainSetIOThreadCount
+ * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN) + * Each bit set to 1 means that corresponding CPU is usable. + * Bytes are stored in little-endian order: CPU0-7, 8-15... + * In each byte, lowest CPU number is least significant bit. + * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in + * underlying virtualization system (Xen...). + * If maplen < size, missing bytes are set to zero. + * If maplen > size, failure code is returned. + * @flags: bitwise-OR of supported virDomainIOThreadsFlags + * + * If the VIR_DOMAIN_IOTHREADS_PIN flag is set, the @iothread_val will be + * an existing IOThread to be pinned
This doesn't feel like a flag. It changes what the API does, not how it does it. Jan

On 03/04/2015 01:00 PM, Ján Tomko wrote:
On Tue, Feb 17, 2015 at 04:03:54PM -0500, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1135491
Add the libvirt API infrastructure to support setting IOThread data. For now this is the pinned CPU information, but eventually will also support hot plug add/del
The API will support the LIVE, CONFIG, or CURRENT flags. If the VIR_DOMAIN_IOTHREADS_PIN flag is not provided, it's left up to the hypervisor to handle, but when not provided the cpumap/maplen arguments are not checked.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 16 ++++++++++ src/driver-hypervisor.h | 8 +++++ src/libvirt-domain.c | 69 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 94 insertions(+)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 9dcc424..e07db16 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -1582,11 +1582,27 @@ struct _virDomainIOThreadsInfo { char **resources; /* array of resources using IOThread */ };
+/* Flags for controlling virtual IOThread pinning. */ +typedef enum { + /* See virDomainModificationImpact for these flags. */ + VIR_DOMAIN_IOTHREADS_CURRENT = VIR_DOMAIN_AFFECT_CURRENT, + VIR_DOMAIN_IOTHREADS_LIVE = VIR_DOMAIN_AFFECT_LIVE, + VIR_DOMAIN_IOTHREADS_CONFIG = VIR_DOMAIN_AFFECT_CONFIG, + + /* Additionally, these flags may be bitwise-OR'd in. */ + VIR_DOMAIN_IOTHREADS_PIN = (1 << 2), /* thread_id to pin using cpumap */ +} virDomainIOThreadsFlags; + void virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info);
int virDomainGetIOThreadsInfo(virDomainPtr domain, virDomainIOThreadsInfoPtr **info, unsigned int flags); +int virDomainSetIOThreads(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags);
/** * VIR_USE_CPU: diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 2ce1a51..120d761 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -386,6 +386,13 @@ typedef int unsigned int flags);
typedef int +(*virDrvDomainSetIOThreads)(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags); + +typedef int (*virDrvDomainGetSecurityLabel)(virDomainPtr domain, virSecurityLabelPtr seclabel);
@@ -1260,6 +1267,7 @@ struct _virHypervisorDriver { virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; virDrvDomainGetIOThreadsInfo domainGetIOThreadsInfo; + virDrvDomainSetIOThreads domainSetIOThreads; virDrvDomainGetSecurityLabel domainGetSecurityLabel; virDrvDomainGetSecurityLabelList domainGetSecurityLabelList; virDrvNodeGetSecurityModel nodeGetSecurityModel; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index f893b35..bf73773 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -7969,6 +7969,75 @@ virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info)
/** + * virDomainSetIOThreads: + * @domain: a domain object + * @iothread_val: either the thread_id to modify or a count of IOThreads + * to be added or removed from the domain depending on the @flags setting
IMO this would look nicer as two separate APIs: virDomainSetIOThreadPin virDomainSetIOThreadCount
OK - fair enough. I was hoping to be able to "reuse" code, but changing this to SetIOThreadPin is fine. The other API will end up being the Add/Remove an IOThread, but will require a bit of internal plumbing changes to allow for a removal of a thread in the middle of the array. Tks - John
+ * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN) + * Each bit set to 1 means that corresponding CPU is usable. + * Bytes are stored in little-endian order: CPU0-7, 8-15... + * In each byte, lowest CPU number is least significant bit. + * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in + * underlying virtualization system (Xen...). + * If maplen < size, missing bytes are set to zero. + * If maplen > size, failure code is returned. + * @flags: bitwise-OR of supported virDomainIOThreadsFlags + * + * If the VIR_DOMAIN_IOTHREADS_PIN flag is set, the @iothread_val will be + * an existing IOThread to be pinned
This doesn't feel like a flag. It changes what the API does, not how it does it.
Jan

On Tue, Feb 17, 2015 at 04:03:54PM -0500, John Ferlan wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1135491
Add the libvirt API infrastructure to support setting IOThread data. For now this is the pinned CPU information, but eventually will also support hot plug add/del
The API will support the LIVE, CONFIG, or CURRENT flags. If the VIR_DOMAIN_IOTHREADS_PIN flag is not provided, it's left up to the hypervisor to handle, but when not provided the cpumap/maplen arguments are not checked.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 16 ++++++++++ src/driver-hypervisor.h | 8 +++++ src/libvirt-domain.c | 69 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 94 insertions(+)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 9dcc424..e07db16 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -1582,11 +1582,27 @@ struct _virDomainIOThreadsInfo { char **resources; /* array of resources using IOThread */ };
+/* Flags for controlling virtual IOThread pinning. */ +typedef enum { + /* See virDomainModificationImpact for these flags. */ + VIR_DOMAIN_IOTHREADS_CURRENT = VIR_DOMAIN_AFFECT_CURRENT, + VIR_DOMAIN_IOTHREADS_LIVE = VIR_DOMAIN_AFFECT_LIVE, + VIR_DOMAIN_IOTHREADS_CONFIG = VIR_DOMAIN_AFFECT_CONFIG, + + /* Additionally, these flags may be bitwise-OR'd in. */ + VIR_DOMAIN_IOTHREADS_PIN = (1 << 2), /* thread_id to pin using cpumap */ +} virDomainIOThreadsFlags; + void virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info);
int virDomainGetIOThreadsInfo(virDomainPtr domain, virDomainIOThreadsInfoPtr **info, unsigned int flags); +int virDomainSetIOThreads(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags);
/** * VIR_USE_CPU: diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 2ce1a51..120d761 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -386,6 +386,13 @@ typedef int unsigned int flags);
typedef int +(*virDrvDomainSetIOThreads)(virDomainPtr domain, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags); + +typedef int (*virDrvDomainGetSecurityLabel)(virDomainPtr domain, virSecurityLabelPtr seclabel);
@@ -1260,6 +1267,7 @@ struct _virHypervisorDriver { virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; virDrvDomainGetIOThreadsInfo domainGetIOThreadsInfo; + virDrvDomainSetIOThreads domainSetIOThreads; virDrvDomainGetSecurityLabel domainGetSecurityLabel; virDrvDomainGetSecurityLabelList domainGetSecurityLabelList; virDrvNodeGetSecurityModel nodeGetSecurityModel; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index f893b35..bf73773 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -7969,6 +7969,75 @@ virDomainIOThreadsInfoFree(virDomainIOThreadsInfoPtr info)
/** + * virDomainSetIOThreads: + * @domain: a domain object + * @iothread_val: either the thread_id to modify or a count of IOThreads + * to be added or removed from the domain depending on the @flags setting + * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN) + * Each bit set to 1 means that corresponding CPU is usable. + * Bytes are stored in little-endian order: CPU0-7, 8-15... + * In each byte, lowest CPU number is least significant bit. + * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in + * underlying virtualization system (Xen...). + * If maplen < size, missing bytes are set to zero. + * If maplen > size, failure code is returned. + * @flags: bitwise-OR of supported virDomainIOThreadsFlags + * + * If the VIR_DOMAIN_IOTHREADS_PIN flag is set, the @iothread_val will be + * an existing IOThread to be pinned
I don't really like the sound of this flags & iothread_val usage. Using flags as a way to multiple different logical operations into one public API is not very nice design and ends up getting confusing for developers & ourselves. IMHO we should have a virDomainSetIOThreadsPin() API for pinning, and another API for adjusting the number of IO threads to be used. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

https://bugzilla.redhat.com/show_bug.cgi?id=1135491 Add the remote plumbing to support SET_IOTHREADS. The code can be automagically generated since there's nothing special on the way in or way out. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 18 +++++++++++++++++- src/remote_protocol-structs | 10 ++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e7fa843..ec49f5d 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8118,6 +8118,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGetVcpus = remoteDomainGetVcpus, /* 0.3.0 */ .domainGetMaxVcpus = remoteDomainGetMaxVcpus, /* 0.3.0 */ .domainGetIOThreadsInfo = remoteDomainGetIOThreadsInfo, /* 1.2.13 */ + .domainSetIOThreads = remoteDomainSetIOThreads, /* 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 63fe547..f7033d7 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1203,6 +1203,14 @@ struct remote_domain_get_iothreads_info_ret { unsigned int ret; }; +struct remote_domain_set_iothreads_args { + remote_nonnull_domain dom; + unsigned int iothreads_val; + opaque cpumap<REMOTE_CPUMAP_MAX>; /* (unsigned char *) */ + unsigned int flags; +}; + + struct remote_domain_get_security_label_args { remote_nonnull_domain dom; }; @@ -5597,5 +5605,13 @@ enum remote_procedure { * @generate: none * @acl: domain:read */ - REMOTE_PROC_DOMAIN_GET_IOTHREADS_INFO = 351 + REMOTE_PROC_DOMAIN_GET_IOTHREADS_INFO = 351, + + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE + * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG + */ + REMOTE_PROC_DOMAIN_SET_IOTHREADS = 352 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index e6cb5b9..d5175d7 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -829,6 +829,15 @@ struct remote_domain_get_iothreads_info_ret { } info; u_int ret; }; +struct remote_domain_set_iothreads_args { + remote_nonnull_domain dom; + u_int iothreads_val; + struct { + u_int cpumap_len; + char * cpumap_val; + } cpumap; + u_int flags; +}; struct remote_domain_get_security_label_args { remote_nonnull_domain dom; }; @@ -2986,4 +2995,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_FSINFO = 349, REMOTE_PROC_DOMAIN_DEFINE_XML_FLAGS = 350, REMOTE_PROC_DOMAIN_GET_IOTHREADS_INFO = 351, + REMOTE_PROC_DOMAIN_SET_IOTHREADS = 352, }; -- 2.1.0

https://bugzilla.redhat.com/show_bug.cgi?id=1135491 More or less a virtual copy of the existing virDomainVcpuPin{Add|Del} API's. NB: The IOThreads implementation "reused" the virDomainVcpuPinDefPtr since it provided everything necessary - an "id" and a "map" for each thread id configured. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/domain_conf.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 10 ++++++++ src/libvirt_private.syms | 2 ++ 3 files changed, 76 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b13cae8..3892096 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -16664,6 +16664,70 @@ virDomainEmulatorPinDel(virDomainDefPtr def) return 0; } +int +virDomainIOThreadsPinAdd(virDomainVcpuPinDefPtr **iothreadspin_list, + size_t *niothreadspin, + unsigned char *cpumap, + int maplen, + int iothread_val) +{ + /* IOThreads share the virDomainVcpuPinDefPtr */ + virDomainVcpuPinDefPtr iothreadpin = NULL; + + if (!iothreadspin_list) + return -1; + + iothreadpin = virDomainVcpuPinFindByVcpu(*iothreadspin_list, + *niothreadspin, + iothread_val); + if (iothreadpin) { + iothreadpin->vcpuid = iothread_val; + virBitmapFree(iothreadpin->cpumask); + iothreadpin->cpumask = virBitmapNewData(cpumap, maplen); + if (!iothreadpin->cpumask) + return -1; + + return 0; + } + + /* No existing iothreadpin matches iothread_val, adding a new one */ + + if (VIR_ALLOC(iothreadpin) < 0) + goto error; + + iothreadpin->vcpuid = iothread_val; + iothreadpin->cpumask = virBitmapNewData(cpumap, maplen); + if (!iothreadpin->cpumask) + goto error; + + if (VIR_APPEND_ELEMENT(*iothreadspin_list, *niothreadspin, iothreadpin) < 0) + goto error; + + return 0; + + error: + virDomainVcpuPinDefFree(iothreadpin); + return -1; +} + +void +virDomainIOThreadsPinDel(virDomainDefPtr def, + int iothread_val) +{ + size_t i; + /* IOThreads share the virDomainVcpuPinDefPtr */ + virDomainVcpuPinDefPtr *iothreadspin_list = def->cputune.iothreadspin; + + for (i = 0; i < def->cputune.niothreadspin; i++) { + if (iothreadspin_list[i]->vcpuid == iothread_val) { + virBitmapFree(iothreadspin_list[i]->cpumask); + VIR_DELETE_ELEMENT(def->cputune.iothreadspin, i, + def->cputune.niothreadspin); + return; + } + } +} + static int virDomainEventActionDefFormat(virBufferPtr buf, int type, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 325afa8..81693e9 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2569,6 +2569,16 @@ int virDomainEmulatorPinAdd(virDomainDefPtr def, int virDomainEmulatorPinDel(virDomainDefPtr def); +/* IOThreads share the virDomainVcpuPinDefPtr */ +int virDomainIOThreadsPinAdd(virDomainVcpuPinDefPtr **iothreadspin_list, + size_t *niothreads, + unsigned char *cpumap, + int maplen, + int iothread_val); + +void virDomainIOThreadsPinDel(virDomainDefPtr def, + int iothread_val); + void virDomainRNGDefFree(virDomainRNGDefPtr def); int virDomainDiskIndexByAddress(virDomainDefPtr def, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 46a1613..8d2b4de 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -306,6 +306,8 @@ virDomainHubTypeToString; virDomainHypervTypeFromString; virDomainHypervTypeToString; virDomainInputDefFree; +virDomainIOThreadsPinAdd; +virDomainIOThreadsPinDel; virDomainLeaseDefFree; virDomainLeaseIndex; virDomainLeaseInsert; -- 2.1.0

https://bugzilla.redhat.com/show_bug.cgi?id=1135491 Implement the qemuDomainSetIOThreads... For now it processes the VIR_DOMAIN_IOTHREADS_PIN and calls qemuDomainSetIOThreadsPin in order to process changing to using the new cpumap. The change can be made either LIVE or CONFIG, or both if both flags are supplied The qemuDomainSetIOThreadsPin live path will utilize the iothreadpids array that was initialized at process start/restart in order to set the pinned thread to a specific CPU. The config path will change the config file as directed by the cpumap. This code is mostly a copy of the qemuDomainPinVcpuFlags. Signed-off-by: John Ferlan <jferlan@redhat.com> --- include/libvirt/libvirt-domain.h | 9 ++ src/qemu/qemu_driver.c | 238 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index e07db16..d756b18 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3207,6 +3207,15 @@ typedef void (*virConnectDomainEventDeviceRemovedCallback)(virConnectPtr conn, # define VIR_DOMAIN_TUNABLE_CPU_EMULATORPIN "cputune.emulatorpin" /** + * VIR_DOMAIN_TUNABLE_CPU_IOTHREADSPIN: + * + * Macro represents formatted pinning for one IOThread specified by id which is + * appended to the parameter name, for example "cputune.iothreadpin1", + * as VIR_TYPED_PARAM_STRING. + */ +# define VIR_DOMAIN_TUNABLE_CPU_IOTHREADSPIN "cputune.iothreadpin%u" + +/** * VIR_DOMAIN_TUNABLE_CPU_CPU_SHARES: * * Macro represents proportional weight of the scheduler used on the diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2c9d08c..bc74942 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5821,6 +5821,243 @@ qemuDomainGetIOThreadsInfo(virDomainPtr dom, return ret; } +static int +qemuDomainSetIOThreadsPin(virDomainPtr dom, + virQEMUDriverPtr driver, + virDomainObjPtr vm, + virQEMUDriverConfigPtr cfg, + virDomainDefPtr persistentDef, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags) +{ + int ret = -1; + virBitmapPtr pcpumap = NULL; + bool doReset = false; + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainVcpuPinDefPtr *newIOThreadsPin = NULL; + size_t newIOThreadsPinNum = 0; + virCgroupPtr cgroup_iothread = NULL; + virObjectEventPtr event = NULL; + char paramField[VIR_TYPED_PARAM_FIELD_LENGTH] = ""; + char *str = NULL; + virTypedParameterPtr eventParams = NULL; + int eventNparams = 0; + int eventMaxparams = 0; + + if (!(pcpumap = virBitmapNewData(cpumap, maplen))) + goto cleanup; + + if (virBitmapIsAllClear(pcpumap)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Empty iothread cpumap list for pinning")); + goto cleanup; + } + + /* pinning to all physical cpus means resetting, + * so check if we can reset setting. + */ + if (virBitmapIsAllSet(pcpumap)) + doReset = true; + + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + + if (priv->iothreadpids == NULL) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("IOThread affinity is not supported")); + goto cleanup; + } + + if (iothread_val > priv->niothreadpids) { + virReportError(VIR_ERR_INVALID_ARG, + _("iothread value out of range %d > %d"), + iothread_val, priv->niothreadpids); + goto cleanup; + } + + if (vm->def->cputune.iothreadspin) { + /* The VcpuPinDefCopy works for IOThreads too */ + newIOThreadsPin = + virDomainVcpuPinDefCopy(vm->def->cputune.iothreadspin, + vm->def->cputune.niothreadspin); + if (!newIOThreadsPin) + goto cleanup; + + newIOThreadsPinNum = vm->def->cputune.niothreadspin; + } else { + if (VIR_ALLOC(newIOThreadsPin) < 0) + goto cleanup; + newIOThreadsPinNum = 0; + } + + if (virDomainIOThreadsPinAdd(&newIOThreadsPin, &newIOThreadsPinNum, + cpumap, maplen, iothread_val) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to update iothreadspin")); + virDomainVcpuPinDefArrayFree(newIOThreadsPin, newIOThreadsPinNum); + goto cleanup; + } + + /* Configure the corresponding cpuset cgroup before set affinity. */ + if (virCgroupHasController(priv->cgroup, + VIR_CGROUP_CONTROLLER_CPUSET)) { + if (virCgroupNewIOThread(priv->cgroup, iothread_val, + false, &cgroup_iothread) < 0) + goto cleanup; + if (qemuSetupCgroupIOThreadsPin(cgroup_iothread, + newIOThreadsPin, + newIOThreadsPinNum, + iothread_val) < 0) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("failed to set cpuset.cpus in cgroup" + " for iothread %d"), iothread_val); + goto cleanup; + } + } else { + if (virProcessSetAffinity(priv->iothreadpids[iothread_val], + pcpumap) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("failed to set cpu affinity for IOThread %d"), + iothread_val); + goto cleanup; + } + } + + if (doReset) { + virDomainIOThreadsPinDel(vm->def, iothread_val); + } else { + if (vm->def->cputune.iothreadspin) + virDomainVcpuPinDefArrayFree(vm->def->cputune.iothreadspin, + vm->def->cputune.niothreadspin); + + vm->def->cputune.iothreadspin = newIOThreadsPin; + vm->def->cputune.niothreadspin = newIOThreadsPinNum; + newIOThreadsPin = NULL; + } + + if (newIOThreadsPin) + virDomainVcpuPinDefArrayFree(newIOThreadsPin, newIOThreadsPinNum); + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) + goto cleanup; + + if (snprintf(paramField, VIR_TYPED_PARAM_FIELD_LENGTH, + VIR_DOMAIN_TUNABLE_CPU_IOTHREADSPIN, iothread_val) < 0) { + goto cleanup; + } + + str = virBitmapFormat(pcpumap); + if (virTypedParamsAddString(&eventParams, &eventNparams, + &eventMaxparams, paramField, str) < 0) + goto cleanup; + + event = virDomainEventTunableNewFromDom(dom, eventParams, eventNparams); + } + + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + if (iothread_val > persistentDef->iothreads) { + virReportError(VIR_ERR_INVALID_ARG, + _("iothread value out of range %d > %d"), + iothread_val, persistentDef->iothreads); + goto cleanup; + } + + if (doReset) { + virDomainIOThreadsPinDel(persistentDef, iothread_val); + } else { + if (!persistentDef->cputune.iothreadspin) { + if (VIR_ALLOC(persistentDef->cputune.iothreadspin) < 0) + goto cleanup; + persistentDef->cputune.niothreadspin = 0; + } + if (virDomainIOThreadsPinAdd(&persistentDef->cputune.iothreadspin, + &persistentDef->cputune.niothreadspin, + cpumap, + maplen, + iothread_val) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to update or add iothreadspin xml " + "of a persistent domain")); + goto cleanup; + } + } + + ret = virDomainSaveConfig(cfg->configDir, persistentDef); + goto cleanup; + } + + ret = 0; + + cleanup: + if (cgroup_iothread) + virCgroupFree(&cgroup_iothread); + if (event) + qemuDomainEventQueue(driver, event); + VIR_FREE(str); + virBitmapFree(pcpumap); + + return ret; +} + +static int +qemuDomainSetIOThreads(virDomainPtr dom, + unsigned int iothread_val, + unsigned char *cpumap, + int maplen, + unsigned int flags) +{ + int ret = -1; + virQEMUDriverPtr driver = dom->conn->privateData; + virQEMUDriverConfigPtr cfg = NULL; + virDomainObjPtr vm; + virCapsPtr caps = NULL; + virDomainDefPtr persistentDef = NULL; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG | + VIR_DOMAIN_IOTHREADS_PIN, -1); + + cfg = virQEMUDriverGetConfig(driver); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainSetIOThreadsEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + if (!(caps = virQEMUDriverGetCapabilities(driver, false))) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainLiveConfigHelperMethod(caps, driver->xmlopt, vm, &flags, + &persistentDef) < 0) + goto endjob; + + if (flags & VIR_DOMAIN_AFFECT_LIVE) + persistentDef = vm->def; + + /* Coverity didn't realize that targetDef must be set if we got here. */ + sa_assert(persistentDef); + + if (flags & VIR_DOMAIN_IOTHREADS_PIN) { + ret = qemuDomainSetIOThreadsPin(dom, driver, vm, cfg, + persistentDef, iothread_val, + cpumap, maplen, flags); + } + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + qemuDomObjEndAPI(&vm); + virObjectUnref(caps); + virObjectUnref(cfg); + return ret; +} + static int qemuDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr seclabel) { virQEMUDriverPtr driver = dom->conn->privateData; @@ -19422,6 +19659,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainGetVcpus = qemuDomainGetVcpus, /* 0.4.4 */ .domainGetMaxVcpus = qemuDomainGetMaxVcpus, /* 0.4.4 */ .domainGetIOThreadsInfo = qemuDomainGetIOThreadsInfo, /* 1.2.13 */ + .domainSetIOThreads = qemuDomainSetIOThreads, /* 1.2.13 */ .domainGetSecurityLabel = qemuDomainGetSecurityLabel, /* 0.6.1 */ .domainGetSecurityLabelList = qemuDomainGetSecurityLabelList, /* 0.10.0 */ .nodeGetSecurityModel = qemuNodeGetSecurityModel, /* 0.6.1 */ -- 2.1.0

https://bugzilla.redhat.com/show_bug.cgi?id=1135491 $ virsh iothreads --help NAME iothreads - view domain IOThreads SYNOPSIS iothreads <domain> [--iothread_id <number>] [--cpulist <string>] [--config] [--live] [--current] DESCRIPTION Returns basic information about the domain IOThreads. OPTIONS [--domain] <string> domain name, id or uuid --iothread_id <number> iothread_id number --cpulist <string> host cpu number(s) to set affinity --config affect next boot --live affect running domain --current affect current domain The changes will now also allow viewing just one iothread if so desired, such as: $ virsh iothreads f18iothr 2 IOThread ID CPU Affinity Resource(s) ----------------------------------------------------------------- 2 3 /home/vm-images/iothr-vol1 In order to change the IOThread pinning, the following would be used: $ virsh iothreads f18iothr 2 0,1 Then to view the change: $ virsh iothreads f18iothr 2 IOThread ID CPU Affinity Resource(s) ----------------------------------------------------------------- 2 0-1 /home/vm-images/iothr-vol1 $ If an invalid value is supplied, an error will be displayed $ virsh iothreads f18iothr 4 3 error: invalid argument: iothread value out of range 4 > 3 $ Signed-off-by: John Ferlan <jferlan@redhat.com> --- tools/virsh-domain.c | 99 ++++++++++++++++++++++++++++++++++++++++------------ tools/virsh.pod | 24 ++++++++----- 2 files changed, 92 insertions(+), 31 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index ffb0392..66757eb 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -6814,6 +6814,15 @@ static const vshCmdOptDef opts_iothreads[] = { .flags = VSH_OFLAG_REQ, .help = N_("domain name, id or uuid") }, + {.name = "iothread_id", + .type = VSH_OT_INT, + .help = N_("iothread_id number") + }, + {.name = "cpulist", + .type = VSH_OT_STRING, + .flags = VSH_OFLAG_EMPTY_OK, + .help = N_("host cpu number(s) to set affinity") + }, {.name = "config", .type = VSH_OT_BOOL, .help = N_("affect next boot") @@ -6840,6 +6849,13 @@ cmdIOThreadsInfo(vshControl *ctl, const vshCmd *cmd) virDomainIOThreadsInfoPtr *info; size_t i, j; int maxcpu; + bool ret = false; + bool query = false; /* Query mode if no cpulist */ + unsigned int iothread_val = 0; + int got_iothread; + unsigned char *cpumap = NULL; + size_t cpumaplen; + const char *cpulist = NULL; unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; VSH_EXCLUSIVE_OPTIONS_VAR(current, live); @@ -6850,45 +6866,82 @@ cmdIOThreadsInfo(vshControl *ctl, const vshCmd *cmd) if (live) flags |= VIR_DOMAIN_AFFECT_LIVE; + if (vshCommandOptStringReq(ctl, cmd, "cpulist", &cpulist) < 0) + return false; + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) return false; - if ((maxcpu = vshNodeGetCPUCount(ctl->conn)) < 0) - goto cleanup; + query = !cpulist; - if ((niothreads = virDomainGetIOThreadsInfo(dom, &info, flags)) < 0) { - vshError(ctl, _("Unable to get domain IOThreads information")); + if ((got_iothread = vshCommandOptUInt(cmd, "iothread_id", + &iothread_val)) < 0) { + vshError(ctl, "%s", _("iothreads: Invalid IOThread number.")); goto cleanup; } - if (niothreads == 0) { - vshError(ctl, _("No IOThreads found for the domain")); + /* In set mode, "iothread_val" is necessary */ + if (!query && got_iothread == 0) { + vshError(ctl, "%s", _("iothreads: set missing IOThreads number.")); goto cleanup; } - vshPrintExtra(ctl, " %-15s %-15s %-15s\n", - _("IOThread ID"), _("CPU Affinity"), _("Resource(s)")); - vshPrintExtra(ctl, "-----------------------------------------------------------------\n"); - for (i = 0; i < niothreads; i++) { + if ((maxcpu = vshNodeGetCPUCount(ctl->conn)) < 0) + goto cleanup; - vshPrintExtra(ctl, " %-15u ", info[i]->iothread_id); - ignore_value(vshPrintPinInfo(info[i]->cpumap, info[i]->cpumaplen, - maxcpu, 0)); - for (j = 0; j < info[i]->nresources; j++) { - if (j == 0) - vshPrintExtra(ctl, "\t\t %s", info[i]->resources[j]); - else - vshPrintExtra(ctl, "\n %-15s %s\t\t %s", - " ", " ", info[i]->resources[j]); + /* Query mode: show CPU affinity information then exit.*/ + if (query) { + if ((niothreads = virDomainGetIOThreadsInfo(dom, &info, flags)) < 0) { + vshError(ctl, _("Unable to get domain IOThreads information")); + goto cleanup; } - vshPrint(ctl, "\n"); - virDomainIOThreadsInfoFree(info[i]); + + if (niothreads == 0) { + vshError(ctl, _("No IOThreads found for the domain")); + ret = true; + goto cleanup; + } + + vshPrintExtra(ctl, " %-15s %-15s %-15s\n", + _("IOThread ID"), _("CPU Affinity"), _("Resource(s)")); + vshPrintExtra(ctl, "-----------------------------------------------------------------\n"); + for (i = 0; i < niothreads; i++) { + + if (iothread_val && (i + 1) != iothread_val) + continue; + vshPrintExtra(ctl, " %-15u ", info[i]->iothread_id); + ignore_value(vshPrintPinInfo(info[i]->cpumap, info[i]->cpumaplen, + maxcpu, 0)); + for (j = 0; j < info[i]->nresources; j++) { + if (j == 0) + vshPrintExtra(ctl, "\t\t %s", info[i]->resources[j]); + else + vshPrintExtra(ctl, "\n %-15s %s\t\t %s", + " ", " ", info[i]->resources[j]); + } + vshPrint(ctl, "\n"); + virDomainIOThreadsInfoFree(info[i]); + } + VIR_FREE(info); + ret = true; + goto cleanup; } - VIR_FREE(info); + + /* We are pinning specified iothread_val to specified physical cpus */ + cpumaplen = VIR_CPU_MAPLEN(maxcpu); + if (!(cpumap = vshParseCPUList(ctl, cpulist, maxcpu, cpumaplen))) + goto cleanup; + + flags |= VIR_DOMAIN_IOTHREADS_PIN; + if (virDomainSetIOThreads(dom, iothread_val, + cpumap, cpumaplen, flags) < 0) + goto cleanup; + + ret = true; cleanup: virDomainFree(dom); - return niothreads >= 0; + return ret; } /* diff --git a/tools/virsh.pod b/tools/virsh.pod index 22e988b..8b75476 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1360,17 +1360,25 @@ 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>]] +=item B<iothreads> I<domain> [I<iothread_id>] [I<cpulist>] +[[I<--live>] [I<--config>] | [I<--current>]] -Display basic domain IOThreads information including the IOThread ID, -the CPU Affinity, and Resources assigned to each IOThread. +Query or change the IOThreads information for the domain. To display the +basic IOThread information including the IOThread ID, CPU Affinity, and +Resources associated with each IOThread omit both the I<iothread_id> and +I<cpulist>. -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. +The I<iothread_id> may be used to query a specific IOThread ID or it can +be omitted to list all the IOThreads for the domain. It must be used in +order to set the I<cpulist> for a specific IOThread ID. +See B<vcpupin> for I<cpulist>. +If I<--live> is specified, affect a running guest. If the guest is not running, +an error is returned. +If I<--config> is specified, affect 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. +affect the current guest state. +Both I<--live> and I<--config> flags may be given if I<cpulist> is present, +but I<--current> is exclusive. =item B<managedsave> I<domain> [I<--bypass-cache>] [{I<--running> | I<--paused>}] [I<--verbose>] -- 2.1.0

On 02/17/2015 04:03 PM, John Ferlan wrote:
v2 here: http://www.redhat.com/archives/libvir-list/2015-February/msg00562.html
Changes over v2: Patches 1-4 * Add "resources" and "nresources" in order to return the resources that are using an IOThread * Fixed memory leak - neglected to free the qemuMonitorIOThreadsInfoPtr 'iothreads' in patch 3
Patches 5-9 (NEW) * Patches 5 & 6 implement the API/remote protocol for virDomainSetIOThreads * Patch 7 implements a couple of helper function - copies of existing vcpupin functions * Patch 8 implements the qemu backend qemuDomainSetIOThreads in order to allow setting IOThread pin data either for live or config. Heavily lifted from existing vcpu code * Patch 9 implements the virsh command options in order to set the pinning on a specific thread. As a side effect/benefit one can now display just one IOThread if so desired.
All the changes are sufficient to satisfy the following bz:
https://bugzilla.redhat.com/show_bug.cgi?id=1135491
John Ferlan (9): Implement public API for virDomainGetIOThreadsInfo remote: Implement the remote plumbing for virDomainGetIOThreadsInfo qemu: Implement the qemu driver fetch for IOThreads virsh: Add 'iothreads' command Implement public API for virDomainSetIOThreads remote: Implement remote plumbing for virDomainSetIOThreads domain: Introduce virDomainIOThreadsPin{Add|Del} qemu: Add qemuDomainSetIOThreads virsh: Allow setting of the iothread pin
daemon/remote.c | 99 +++++++- include/libvirt/libvirt-domain.h | 48 +++- src/conf/domain_conf.c | 64 +++++ src/conf/domain_conf.h | 10 + src/driver-hypervisor.h | 16 +- src/libvirt-domain.c | 149 ++++++++++- src/libvirt_private.syms | 2 + src/libvirt_public.syms | 7 + src/qemu/qemu_driver.c | 519 +++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 94 ++++++- src/remote/remote_protocol.x | 48 +++- src/remote_protocol-structs | 33 +++ src/rpc/gendispatch.pl | 1 + tools/virsh-domain.c | 154 ++++++++++++ tools/virsh.pod | 20 ++ 15 files changed, 1257 insertions(+), 7 deletions(-)
ping - although probably now not a 1.2.13 candidate with the freeze - if a review can be completed I could get it in early to 1.2.14... tks - John

On 02/24/2015 07:01 AM, John Ferlan wrote:
On 02/17/2015 04:03 PM, John Ferlan wrote:
v2 here: http://www.redhat.com/archives/libvir-list/2015-February/msg00562.html
Changes over v2: Patches 1-4 * Add "resources" and "nresources" in order to return the resources that are using an IOThread * Fixed memory leak - neglected to free the qemuMonitorIOThreadsInfoPtr 'iothreads' in patch 3
Patches 5-9 (NEW) * Patches 5 & 6 implement the API/remote protocol for virDomainSetIOThreads * Patch 7 implements a couple of helper function - copies of existing vcpupin functions * Patch 8 implements the qemu backend qemuDomainSetIOThreads in order to allow setting IOThread pin data either for live or config. Heavily lifted from existing vcpu code * Patch 9 implements the virsh command options in order to set the pinning on a specific thread. As a side effect/benefit one can now display just one IOThread if so desired.
All the changes are sufficient to satisfy the following bz:
https://bugzilla.redhat.com/show_bug.cgi?id=1135491
John Ferlan (9): Implement public API for virDomainGetIOThreadsInfo remote: Implement the remote plumbing for virDomainGetIOThreadsInfo qemu: Implement the qemu driver fetch for IOThreads virsh: Add 'iothreads' command Implement public API for virDomainSetIOThreads remote: Implement remote plumbing for virDomainSetIOThreads domain: Introduce virDomainIOThreadsPin{Add|Del} qemu: Add qemuDomainSetIOThreads virsh: Allow setting of the iothread pin
daemon/remote.c | 99 +++++++- include/libvirt/libvirt-domain.h | 48 +++- src/conf/domain_conf.c | 64 +++++ src/conf/domain_conf.h | 10 + src/driver-hypervisor.h | 16 +- src/libvirt-domain.c | 149 ++++++++++- src/libvirt_private.syms | 2 + src/libvirt_public.syms | 7 + src/qemu/qemu_driver.c | 519 +++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 94 ++++++- src/remote/remote_protocol.x | 48 +++- src/remote_protocol-structs | 33 +++ src/rpc/gendispatch.pl | 1 + tools/virsh-domain.c | 154 ++++++++++++ tools/virsh.pod | 20 ++ 15 files changed, 1257 insertions(+), 7 deletions(-)
ping - although probably now not a 1.2.13 candidate with the freeze - if a review can be completed I could get it in early to 1.2.14...
Now that 1.2.13 is out - I know I have to change to using 1.2.14, but hopefully someone can review, so I can start on the hotplug options of iothreads. tks John
participants (4)
-
Daniel P. Berrange
-
John Ferlan
-
Ján Tomko
-
Laine Stump