[libvirt] [PATCH] Get thread and socket information in virsh nodeinfo.
Daniel Veillard
veillard at redhat.com
Mon Mar 8 12:45:50 UTC 2010
On Fri, Mar 05, 2010 at 12:03:51PM -0500, Chris Lalancette wrote:
> The current code for "nodeinfo" is pretty naive
> about socket and thread information. To determine the
> sockets, it just takes the number of cpus and divides
> by the number of cores. For the thread count, it always
> sets it to 1. With more recent Intel machines, however,
> hyperthreading is again an option, meaning that these
> heuristics no longer work and give bogus numbers. This
> patch goes through /sys to get the additional
> information so we properly report it.
>
> Note that I had to edit the tests not to report on
> socket and thread counts, since these are determined
> dynamically now.
>
> Signed-off-by: Chris Lalancette <clalance at redhat.com>
> ---
> src/nodeinfo.c | 140 +++++++++++++++++++++++++++++--
> tests/nodeinfodata/linux-nodeinfo-1.txt | 2 +-
> tests/nodeinfodata/linux-nodeinfo-2.txt | 2 +-
> tests/nodeinfodata/linux-nodeinfo-3.txt | 2 +-
> tests/nodeinfodata/linux-nodeinfo-4.txt | 2 +-
> tests/nodeinfodata/linux-nodeinfo-5.txt | 2 +-
> tests/nodeinfodata/linux-nodeinfo-6.txt | 2 +-
> tests/nodeinfotest.c | 5 +-
> 8 files changed, 139 insertions(+), 18 deletions(-)
>
> diff --git a/src/nodeinfo.c b/src/nodeinfo.c
> index 2d44609..18efd28 100644
> --- a/src/nodeinfo.c
> +++ b/src/nodeinfo.c
> @@ -28,6 +28,7 @@
> #include <stdlib.h>
> #include <stdint.h>
> #include <errno.h>
> +#include <dirent.h>
>
> #if HAVE_NUMACTL
> # define NUMA_VERSION1_COMPATIBILITY 1
> @@ -55,22 +56,117 @@
>
> #ifdef __linux__
> #define CPUINFO_PATH "/proc/cpuinfo"
> +#define CPU_SYS_PATH "/sys/devices/system/cpu"
>
> -/* NB, these are not static as we need to call them from testsuite */
> +/* NB, this is not static as we need to call it from the testsuite */
> int linuxNodeInfoCPUPopulate(virConnectPtr conn, FILE *cpuinfo,
> virNodeInfoPtr nodeinfo);
>
> -int linuxNodeInfoCPUPopulate(virConnectPtr conn, FILE *cpuinfo, virNodeInfoPtr nodeinfo) {
> +static int popcnt(char x)
> +{
> + char count;
> + for (count = 0; x; count++)
> + x &= x-1;
> + return count;
> +}
> +
> +static unsigned long count_thread_siblings(int cpu)
> +{
> + unsigned long ret = 0;
> + char *path = NULL;
> + FILE *pathfp = NULL;
> + char str[1024];
> + int i;
> +
> + if (virAsprintf(&path, "%s/cpu%d/topology/thread_siblings", CPU_SYS_PATH,
> + cpu) < 0) {
> + virReportOOMError();
> + return 0;
> + }
> +
> + pathfp = fopen(path, "r");
> + if (pathfp == NULL) {
> + virReportSystemError(errno, _("cannot open %s"), path);
> + VIR_FREE(path);
> + return 0;
> + }
> +
> + if (fgets(str, sizeof(str), pathfp) == NULL) {
> + virReportSystemError(errno, _("cannot read from %s"), path);
> + goto cleanup;
> + }
> +
> + i = 0;
> + while (str[i] != '\0') {
> + if (str[i] != '\n' && str[i] != ',')
> + ret += popcnt(str[i] - '0');
> + i++;
> + }
> +
> +cleanup:
> + fclose(pathfp);
> + VIR_FREE(path);
> +
> + return ret;
> +}
> +
> +static int parse_socket(int cpu)
> +{
> + char *path = NULL;
> + FILE *pathfp;
> + char socket_str[1024];
> + char *tmp;
> + int socket;
> +
> + if (virAsprintf(&path, "%s/cpu%d/topology/physical_package_id",
> + CPU_SYS_PATH, cpu) < 0) {
> + virReportOOMError();
> + return -1;
> + }
> +
> + pathfp = fopen(path, "r");
> + if (pathfp == NULL) {
> + virReportSystemError(errno, _("cannot open %s"), path);
> + goto cleanup;
> + }
> +
> + if (fgets(socket_str, sizeof(socket_str), pathfp) == NULL) {
> + virReportSystemError(errno, _("cannot read from %s"), path);
> + goto cleanup;
> + }
> + if (virStrToLong_i(socket_str, &tmp, 10, &socket) < 0) {
> + nodeReportError(NULL, VIR_ERR_INTERNAL_ERROR,
> + _("could not convert '%s' to an integer"),
> + socket_str);
> + goto cleanup;
> + }
> +
> +cleanup:
> + fclose(pathfp);
> + VIR_FREE(path);
> +
> + return socket;
> +}
> +
> +int linuxNodeInfoCPUPopulate(virConnectPtr conn, FILE *cpuinfo,
> + virNodeInfoPtr nodeinfo)
> +{
> char line[1024];
> + DIR *cpudir = NULL;
> + struct dirent *cpudirent = NULL;
> + int cpu;
> + unsigned long cur_threads;
> + int socket;
> + unsigned long long socket_mask = 0;
>
> nodeinfo->cpus = 0;
> nodeinfo->mhz = 0;
> - nodeinfo->nodes = nodeinfo->sockets = nodeinfo->cores = nodeinfo->threads = 1;
> + nodeinfo->nodes = nodeinfo->cores = 1;
>
> /* NB: It is impossible to fill our nodes, since cpuinfo
> * has not knowledge of NUMA nodes */
>
> - /* XXX hyperthreads */
> + /* NOTE: hyperthreads are ignored here; they are parsed out of /sys */
> while (fgets(line, sizeof(line), cpuinfo) != NULL) {
> char *buf = line;
> if (STRPREFIX(buf, "processor")) { /* aka a single logical CPU */
> @@ -122,12 +218,38 @@ int linuxNodeInfoCPUPopulate(virConnectPtr conn, FILE *cpuinfo, virNodeInfoPtr n
> return -1;
> }
>
> - /*
> - * Can't reliably count sockets from proc metadata, so
> - * infer it based on total CPUs vs cores.
> - * XXX hyperthreads
> + /* OK, we've parsed what we can out of /proc/cpuinfo. Get the socket
> + * and thread information from /sys
> */
> - nodeinfo->sockets = nodeinfo->cpus / nodeinfo->cores;
> + cpudir = opendir(CPU_SYS_PATH);
> + if (cpudir == NULL) {
> + virReportSystemError(errno, _("cannot opendir %s"), CPU_SYS_PATH);
> + return -1;
> + }
> + while ((cpudirent = readdir(cpudir))) {
> + if (sscanf(cpudirent->d_name, "cpu%d", &cpu) != 1)
> + continue;
> +
> + socket = parse_socket(cpu);
> + if (socket < 0) {
> + closedir(cpudir);
> + return -1;
> + }
> + if (!(socket_mask & (1 << socket))) {
> + socket_mask |= (1 << socket);
> + nodeinfo->sockets++;
> + }
> +
> + cur_threads = count_thread_siblings(cpu);
> + if (cur_threads == 0) {
> + closedir(cpudir);
> + return -1;
> + }
> + if (cur_threads > nodeinfo->threads)
> + nodeinfo->threads = cur_threads;
> + }
> +
> + closedir(cpudir);
>
> return 0;
> }
> diff --git a/tests/nodeinfodata/linux-nodeinfo-1.txt b/tests/nodeinfodata/linux-nodeinfo-1.txt
> index e52e20a..09e2946 100644
> --- a/tests/nodeinfodata/linux-nodeinfo-1.txt
> +++ b/tests/nodeinfodata/linux-nodeinfo-1.txt
> @@ -1 +1 @@
> -CPUs: 2, MHz: 2800, Nodes: 1, Sockets: 1, Cores: 2, Threads: 1
> +CPUs: 2, MHz: 2800, Nodes: 1, Cores: 2
> diff --git a/tests/nodeinfodata/linux-nodeinfo-2.txt b/tests/nodeinfodata/linux-nodeinfo-2.txt
> index 12e819b..e4eea94 100644
> --- a/tests/nodeinfodata/linux-nodeinfo-2.txt
> +++ b/tests/nodeinfodata/linux-nodeinfo-2.txt
> @@ -1 +1 @@
> -CPUs: 2, MHz: 2211, Nodes: 1, Sockets: 1, Cores: 2, Threads: 1
> +CPUs: 2, MHz: 2211, Nodes: 1, Cores: 2
> diff --git a/tests/nodeinfodata/linux-nodeinfo-3.txt b/tests/nodeinfodata/linux-nodeinfo-3.txt
> index d285781..17d4d8e 100644
> --- a/tests/nodeinfodata/linux-nodeinfo-3.txt
> +++ b/tests/nodeinfodata/linux-nodeinfo-3.txt
> @@ -1 +1 @@
> -CPUs: 4, MHz: 1595, Nodes: 1, Sockets: 2, Cores: 2, Threads: 1
> +CPUs: 4, MHz: 1595, Nodes: 1, Cores: 2
> diff --git a/tests/nodeinfodata/linux-nodeinfo-4.txt b/tests/nodeinfodata/linux-nodeinfo-4.txt
> index 991d4f9..5a5c919 100644
> --- a/tests/nodeinfodata/linux-nodeinfo-4.txt
> +++ b/tests/nodeinfodata/linux-nodeinfo-4.txt
> @@ -1 +1 @@
> -CPUs: 4, MHz: 1000, Nodes: 1, Sockets: 1, Cores: 4, Threads: 1
> +CPUs: 4, MHz: 1000, Nodes: 1, Cores: 4
> diff --git a/tests/nodeinfodata/linux-nodeinfo-5.txt b/tests/nodeinfodata/linux-nodeinfo-5.txt
> index dce7ada..54abb5d 100644
> --- a/tests/nodeinfodata/linux-nodeinfo-5.txt
> +++ b/tests/nodeinfodata/linux-nodeinfo-5.txt
> @@ -1 +1 @@
> -CPUs: 4, MHz: 2814, Nodes: 1, Sockets: 2, Cores: 2, Threads: 1
> +CPUs: 4, MHz: 2814, Nodes: 1, Cores: 2
> diff --git a/tests/nodeinfodata/linux-nodeinfo-6.txt b/tests/nodeinfodata/linux-nodeinfo-6.txt
> index 75cdaa9..f89e35e 100644
> --- a/tests/nodeinfodata/linux-nodeinfo-6.txt
> +++ b/tests/nodeinfodata/linux-nodeinfo-6.txt
> @@ -1 +1 @@
> -CPUs: 4, MHz: 1000, Nodes: 1, Sockets: 2, Cores: 2, Threads: 1
> +CPUs: 4, MHz: 1000, Nodes: 1, Cores: 2
> diff --git a/tests/nodeinfotest.c b/tests/nodeinfotest.c
> index 4cb248a..b3b91ad 100644
> --- a/tests/nodeinfotest.c
> +++ b/tests/nodeinfotest.c
> @@ -47,9 +47,8 @@ static int linuxTestCompareFiles(const char *cpuinfofile, const char *outputfile
> fclose(cpuinfo);
>
> snprintf(actualData, MAX_FILE,
> - "CPUs: %u, MHz: %u, Nodes: %u, Sockets: %u, Cores: %u, Threads: %u\n",
> - nodeinfo.cpus, nodeinfo.mhz, nodeinfo.nodes, nodeinfo.sockets,
> - nodeinfo.cores, nodeinfo.threads);
> + "CPUs: %u, MHz: %u, Nodes: %u, Cores: %u\n",
> + nodeinfo.cpus, nodeinfo.mhz, nodeinfo.nodes, nodeinfo.cores);
>
> if (STRNEQ(actualData, expectData)) {
> if (getenv("DEBUG_TESTS")) {
ACK, looks fine.
I just hope this wn't break with virt on virtualization, for example
within a Xen virtual machine /sys/devices/system/cpu/cpu0/topology/
is an empty dir (on old RHEL-5 though).
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
daniel at veillard.com | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library http://libvirt.org/
More information about the libvir-list
mailing list