The macro VIR_NODEINFO_MAXCPUS computes the maximum number of supported
CPUs for a node as the product of sockets, cores and threads from the
virNodeInfo structure.
Since topology cannot be discovered for offline CPUs, any of the values
for sockets, cores or threads can be wrong (too small).
As a consequence the per-VCPU CPU masks are truncated incorrectly and
it's impossible to correlate the bits in the masks with actual CPUs on
the host.
Example:
Host has 3 logical CPUs, 1 socket, 3 cores, 1 thread.
Guest has 1 virtual CPU and is started while all 3 host CPUs are
online.
$ virsh vcpuinfo guest
VCPU: 0
CPU: 0
State: running
CPU time: 35.4s
CPU Affinity: yyy
$ echo 0 > /sys/devices/system/cpu/cpu1/online
$ virsh vcpuinfo guest
VCPU: 0
CPU: 0
State: running
CPU time: 35.5s
CPU Affinity: y-
The correct display for CPU affinity would have been y-y, as the guest
continues to use CPUs 0 and 2.
This is not a display problem only, because it is also not possible to
explicitly pin the virtual CPU to host CPUs 0 and 2, due to the
truncated CPU mask.
To prevent this, the following changes have been made:
1. In virNodeParseNode count all logical CPUs, offline or online
and pass them back in a new argument 'maxcpus'.
2. Extend the virNodeInfo struct with a new member 'maxcpus'
containing the number of all logical CPUs, online and offline.
3. Change the macro VIR_NODEINFO_MAXCPUS to return virNodeInfo.maxpus
Signed-off-by: Viktor Mihajlovski <mihajlov(a)linux.vnet.ibm.com>
---
include/libvirt/libvirt.h.in | 3 ++-
src/nodeinfo.c | 17 +++++++++++++----
src/remote/remote_protocol.x | 1 +
src/remote_protocol-structs | 1 +
4 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 81f12a4..16265b5 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -445,6 +445,7 @@ struct _virNodeInfo {
total number of CPU sockets otherwise */
unsigned int cores; /* number of cores per socket */
unsigned int threads;/* number of threads per core */
+ unsigned int maxcpus;/* number of maximum cpus - online and offline */
};
/**
@@ -1039,7 +1040,7 @@ int virDomainMigrateGetMaxSpeed(virDomainPtr domain,
*/
-#define VIR_NODEINFO_MAXCPUS(nodeinfo)
((nodeinfo).nodes*(nodeinfo).sockets*(nodeinfo).cores*(nodeinfo).threads)
+#define VIR_NODEINFO_MAXCPUS(nodeinfo) ((nodeinfo).maxcpus)
/**
* virNodeInfoPtr:
diff --git a/src/nodeinfo.c b/src/nodeinfo.c
index c0e60d8..2e9c111 100644
--- a/src/nodeinfo.c
+++ b/src/nodeinfo.c
@@ -202,9 +202,11 @@ CPU_COUNT(cpu_set_t *set)
/* parses a node entry, returning number of processors in the node and
* filling arguments */
static int
-virNodeParseNode(const char *node, int *sockets, int *cores, int *threads)
+virNodeParseNode(const char *node, int *sockets, int *cores,
+ int *threads, int *cpus)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4)
+ ATTRIBUTE_NONNULL(5)
{
int ret = -1;
int processors = 0;
@@ -223,6 +225,7 @@ virNodeParseNode(const char *node, int *sockets, int *cores, int
*threads)
*threads = 0;
*cores = 0;
*sockets = 0;
+ *cpus = 0;
if (!(cpudir = opendir(node))) {
virReportSystemError(errno, _("cannot opendir %s"), node);
@@ -275,6 +278,8 @@ virNodeParseNode(const char *node, int *sockets, int *cores, int
*threads)
if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1)
continue;
+ (*cpus)++;
+
if ((online = virNodeGetCpuValue(node, cpu, "online", true)) < 0)
goto cleanup;
@@ -348,7 +353,7 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
char line[1024];
DIR *nodedir = NULL;
struct dirent *nodedirent = NULL;
- int cpus, cores, socks, threads;
+ int cpus, cores, socks, threads, maxcpus;
unsigned int node;
int ret = -1;
char *sysfs_nodedir = NULL;
@@ -358,6 +363,7 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
nodeinfo->mhz = 0;
nodeinfo->cores = 0;
nodeinfo->nodes = 0;
+ nodeinfo->maxcpus = 0;
/* Start with parsing CPU clock speed from /proc/cpuinfo */
while (fgets(line, sizeof(line), cpuinfo) != NULL) {
@@ -470,12 +476,13 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
}
if ((cpus = virNodeParseNode(sysfs_cpudir, &socks,
- &cores, &threads)) < 0)
+ &cores, &threads, &maxcpus)) < 0)
goto cleanup;
VIR_FREE(sysfs_cpudir);
nodeinfo->cpus += cpus;
+ nodeinfo->maxcpus += maxcpus;
if (socks > nodeinfo->sockets)
nodeinfo->sockets = socks;
@@ -505,7 +512,8 @@ fallback:
goto cleanup;
}
- if ((cpus = virNodeParseNode(sysfs_cpudir, &socks, &cores, &threads))
< 0)
+ if ((cpus = virNodeParseNode(sysfs_cpudir, &socks, &cores,
+ &threads, &maxcpus)) < 0)
goto cleanup;
nodeinfo->nodes = 1;
@@ -513,6 +521,7 @@ fallback:
nodeinfo->sockets = socks;
nodeinfo->cores = cores;
nodeinfo->threads = threads;
+ nodeinfo->maxcpus = maxcpus;
done:
/* There should always be at least one cpu, socket, node, and thread. */
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index b0b530c..40883d0 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -465,6 +465,7 @@ struct remote_node_get_info_ret { /* insert@1 */
int sockets;
int cores;
int threads;
+ int maxcpus;
};
struct remote_get_capabilities_ret {
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index 4d2627a..8fece92 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -144,6 +144,7 @@ struct remote_node_get_info_ret {
int sockets;
int cores;
int threads;
+ int maxcpus;
};
struct remote_get_capabilities_ret {
remote_nonnull_string capabilities;
--
1.7.0.4