numa_node_to_cpus() uses an internal cache to store the mapping
between NUMA nodes and CPUs (see Bug #1213835), which means
long-running processes such as libvirtd will get stale data if
CPU hotplugging is used during their lifetime.
The function has been rewritten to collect the information
directly from sysfs.
---
src/util/virnuma.c | 158 +++++++++++++++++++++++++++--------------------------
1 file changed, 81 insertions(+), 77 deletions(-)
diff --git a/src/util/virnuma.c b/src/util/virnuma.c
index 669192a..ccc356b 100644
--- a/src/util/virnuma.c
+++ b/src/util/virnuma.c
@@ -1,7 +1,7 @@
/*
* virnuma.c: helper APIs for managing numa
*
- * Copyright (C) 2011-2014 Red Hat, Inc.
+ * Copyright (C) 2011-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
@@ -234,81 +234,6 @@ virNumaGetNodeMemory(int node,
}
-/**
- * virNumaGetNodeCPUs:
- * @node: identifier of the requested NUMA node
- * @cpus: returns a bitmap of CPUs in @node
- *
- * Returns count of CPUs in the selected node and sets the map of the cpus to
- * @cpus. On error if the @node doesn't exist in the system this function
- * returns -2 and sets @cpus to NULL. On other errors -1 is returned, @cpus
- * is set to NULL and an error is reported.
- */
-
-# define n_bits(var) (8 * sizeof(var))
-# define MASK_CPU_ISSET(mask, cpu) \
- (((mask)[((cpu) / n_bits(*(mask)))] >> ((cpu) % n_bits(*(mask)))) & 1)
-int
-virNumaGetNodeCPUs(int node,
- virBitmapPtr *cpus)
-{
- unsigned long *mask = NULL;
- unsigned long *allonesmask = NULL;
- virBitmapPtr cpumap = NULL;
- int ncpus = 0;
- int max_n_cpus = virNumaGetMaxCPUs();
- int mask_n_bytes = max_n_cpus / 8;
- size_t i;
- int ret = -1;
-
- *cpus = NULL;
-
- if (VIR_ALLOC_N(mask, mask_n_bytes / sizeof(*mask)) < 0)
- goto cleanup;
-
- if (VIR_ALLOC_N(allonesmask, mask_n_bytes / sizeof(*mask)) < 0)
- goto cleanup;
-
- memset(allonesmask, 0xff, mask_n_bytes);
-
- /* The first time this returns -1, ENOENT if node doesn't exist... */
- if (numa_node_to_cpus(node, mask, mask_n_bytes) < 0) {
- VIR_WARN("NUMA topology for cell %d is not available, ignoring",
node);
- ret = -2;
- goto cleanup;
- }
-
- /* second, third... times it returns an all-1's mask */
- if (memcmp(mask, allonesmask, mask_n_bytes) == 0) {
- VIR_DEBUG("NUMA topology for cell %d is invalid, ignoring", node);
- ret = -2;
- goto cleanup;
- }
-
- if (!(cpumap = virBitmapNew(max_n_cpus)))
- goto cleanup;
-
- for (i = 0; i < max_n_cpus; i++) {
- if (MASK_CPU_ISSET(mask, i)) {
- ignore_value(virBitmapSetBit(cpumap, i));
- ncpus++;
- }
- }
-
- *cpus = cpumap;
- cpumap = NULL;
- ret = ncpus;
-
- cleanup:
- VIR_FREE(mask);
- VIR_FREE(allonesmask);
- virBitmapFree(cpumap);
-
- return ret;
-}
-# undef MASK_CPU_ISSET
-# undef n_bits
-
#else /* !WITH_NUMACTL */
int
@@ -352,6 +277,84 @@ virNumaGetNodeMemory(int node ATTRIBUTE_UNUSED,
return -1;
}
+#endif /* !WITH_NUMACTL */
+
+#ifdef __linux__
+
+# define SYSFS_SYSTEM_PATH "/sys/devices/system"
+
+/**
+ * virNumaGetNodeCPUs:
+ * @node: identifier of the requested NUMA node
+ * @cpus: returns a bitmap of CPUs in @node
+ *
+ * Returns count of CPUs in the selected node and sets the map of the cpus to
+ * @cpus. On error if the @node doesn't exist in the system this function
+ * returns -2 and sets @cpus to NULL. On other errors -1 is returned, @cpus
+ * is set to NULL and an error is reported.
+ */
+int
+virNumaGetNodeCPUs(int node,
+ virBitmapPtr *cpus)
+{
+ virBitmapPtr cpumap = NULL;
+ char *sysfs_node_path;
+ DIR *node_dir;
+ struct dirent *node_dirent;
+ int direrr;
+ int cpu;
+ int online;
+ int ncpus = 0;
+ int ret = -1;
+
+ if (virAsprintf(&sysfs_node_path, "%s/node/node%d",
+ SYSFS_SYSTEM_PATH, node) < 0)
+ goto out;
+
+ if ((node_dir = opendir(sysfs_node_path)) == NULL) {
+ if (errno == ENOENT)
+ ret = -2;
+ goto cleanup;
+ }
+
+ if ((cpumap = virBitmapNew(virNumaGetMaxCPUs())) == NULL)
+ goto cleanup;
+
+ while ((direrr = virDirRead(node_dir, &node_dirent, sysfs_node_path)) > 0) {
+ if (sscanf(node_dirent->d_name, "cpu%u", &cpu) != 1)
+ continue;
+
+ if ((online = nodeGetCPUValue(sysfs_node_path, cpu, "online", 1)) <
0)
+ goto cleanup;
+
+ if (online) {
+ if (virBitmapSetBit(cpumap, cpu) < 0)
+ goto cleanup;
+ ncpus++;
+ }
+ }
+
+ if (direrr < 0)
+ goto cleanup;
+
+ if (cpus != NULL) {
+ *cpus = cpumap;
+ cpumap = NULL;
+ }
+
+ ret = ncpus;
+
+ cleanup:
+ virBitmapFree(cpumap);
+ if (node_dir != NULL)
+ closedir(node_dir);
+ VIR_FREE(sysfs_node_path);
+
+ out:
+ return ret;
+}
+
+#else /* !__linux__ */
int
virNumaGetNodeCPUs(int node ATTRIBUTE_UNUSED,
@@ -363,7 +366,8 @@ virNumaGetNodeCPUs(int node ATTRIBUTE_UNUSED,
_("NUMA isn't available on this host"));
return -1;
}
-#endif /* !WITH_NUMACTL */
+
+#endif /* !__linux__ */
/**
* virNumaGetMaxCPUs:
--
2.1.0