From aa2f6f96ddd7a57011c3d25586d588100651feb2 Mon Sep 17 00:00:00 2001 From: Chris Lalancette Date: Thu, 4 Mar 2010 17:28:40 -0500 Subject: [PATCH] Get thread and socket information in virsh nodeinfo. 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. v2: As pointed out by Eric Blake, gnulib provides count-one-bits (which is LGPLv2+). Use it instead of a hand-coded popcnt. Signed-off-by: Chris Lalancette --- bootstrap.conf | 1 + src/nodeinfo.c | 133 ++++++++++++++++++++++-- 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 +- 9 files changed, 133 insertions(+), 18 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index 58ef2abd04..157092f9cb 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -25,6 +25,7 @@ c-ctype canonicalize-lgpl close connect +count-one-bits dirname-lgpl getaddrinfo gethostname diff --git a/src/nodeinfo.c b/src/nodeinfo.c index 4648be1f7d..2dab5b29c8 100644 --- a/src/nodeinfo.c +++ b/src/nodeinfo.c @@ -28,6 +28,7 @@ #include #include #include +#include #if HAVE_NUMACTL # define NUMA_VERSION1_COMPATIBILITY 1 @@ -45,6 +46,7 @@ #include "util.h" #include "logging.h" #include "virterror_internal.h" +#include "count-one-bits.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -55,22 +57,109 @@ #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 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 += count_one_bits(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 +211,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 e52e20ab72..09e29464b2 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 12e819ba7d..e4eea943d2 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 d285781507..17d4d8e213 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 991d4f93e0..5a5c919ae0 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 dce7ada3e4..54abb5d39d 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 75cdaa9722..f89e35ee6e 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 4cb248acfb..b3b91ad3b8 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")) {