[libvirt] [RFC v3 PATCH 1/5] PowerPC : Use sysfs to gather host topology, in place of /proc/cpuinfo
Daniel P. Berrange
berrange at redhat.com
Wed Nov 30 12:30:15 UTC 2011
On Tue, Nov 29, 2011 at 08:26:57PM +0530, Prerna Saxena wrote:
> From: Prerna Saxena <prerna at linux.vnet.ibm.com>
> Date: Mon, 3 Oct 2011 05:45:30 -0700
> Subject: [PATCH 1/5] Use sysfs to gather host topology, in place of
> /proc/cpuinfo
>
> Libvirt at present depends on /proc/cpuinfo to gather host
> details such as CPUs, cores, threads, etc. This is an architecture-
> dependent approach. An alternative is to use 'Sysfs', which provides
> a platform-agnostic interface to parse host CPU topology.
>
> Signed-off-by: Prerna Saxena <prerna at linux.vnet.ibm.com>
> ---
> src/nodeinfo.c | 114 +++++++++++++++++++-------------------------------------
> 1 files changed, 39 insertions(+), 75 deletions(-)
>
> diff --git a/src/nodeinfo.c b/src/nodeinfo.c
> index 6448b79..f70654a 100644
> --- a/src/nodeinfo.c
> +++ b/src/nodeinfo.c
> @@ -30,6 +30,7 @@
> #include <errno.h>
> #include <dirent.h>
> #include <sys/utsname.h>
> +#include <sched.h>
>
> #if HAVE_NUMACTL
> # define NUMA_VERSION1_COMPATIBILITY 1
> @@ -67,8 +68,7 @@
>
> /* NB, this is not static as we need to call it from the testsuite */
> int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
> - virNodeInfoPtr nodeinfo,
> - bool need_hyperthreads);
> + virNodeInfoPtr nodeinfo);
>
> static int linuxNodeGetCPUStats(FILE *procstat,
> int cpuNum,
> @@ -191,23 +191,26 @@ static int parse_socket(unsigned int cpu)
> return ret;
> }
>
> +static int parse_core(unsigned int cpu)
> +{
> + return get_cpu_value(cpu, "topology/core_id", false);
> +}
> +
> int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
> - virNodeInfoPtr nodeinfo,
> - bool need_hyperthreads)
> + virNodeInfoPtr nodeinfo)
> {
> char line[1024];
> DIR *cpudir = NULL;
> struct dirent *cpudirent = NULL;
> unsigned int cpu;
> - unsigned long cur_threads;
> - int socket;
> - unsigned long long socket_mask = 0;
> - unsigned int remaining;
> + unsigned long core, socket, cur_threads;
> + cpu_set_t core_mask;
> + cpu_set_t socket_mask;
> int online;
>
> nodeinfo->cpus = 0;
> nodeinfo->mhz = 0;
> - nodeinfo->cores = 1;
> + nodeinfo->cores = 0;
>
> nodeinfo->nodes = 1;
> # if HAVE_NUMACTL
> @@ -221,20 +224,10 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
> /* 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 */
> - buf += 9;
> - while (*buf && c_isspace(*buf))
> - buf++;
> - if (*buf != ':') {
> - nodeReportError(VIR_ERR_INTERNAL_ERROR,
> - "%s", _("parsing cpuinfo processor"));
> - return -1;
> - }
> - nodeinfo->cpus++;
> # if defined(__x86_64__) || \
> defined(__amd64__) || \
> defined(__i386__)
> - } else if (STRPREFIX(buf, "cpu MHz")) {
> + if (STRPREFIX(buf, "cpu MHz")) {
> char *p;
> unsigned int ui;
> buf += 9;
> @@ -249,24 +242,9 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
> /* Accept trailing fractional part. */
> && (*p == '\0' || *p == '.' || c_isspace(*p)))
> nodeinfo->mhz = ui;
> - } else if (STRPREFIX(buf, "cpu cores")) { /* aka cores */
> - char *p;
> - unsigned int id;
> - buf += 9;
> - while (*buf && c_isspace(*buf))
> - buf++;
> - if (*buf != ':' || !buf[1]) {
> - nodeReportError(VIR_ERR_INTERNAL_ERROR,
> - _("parsing cpuinfo cpu cores %c"), *buf);
> - return -1;
> - }
> - if (virStrToLong_ui(buf+1, &p, 10, &id) == 0
> - && (*p == '\0' || c_isspace(*p))
> - && id > nodeinfo->cores)
> - nodeinfo->cores = id;
> # elif defined(__powerpc__) || \
> defined(__powerpc64__)
> - } else if (STRPREFIX(buf, "clock")) {
> + if (STRPREFIX(buf, "clock")) {
> char *p;
> unsigned int ui;
> buf += 5;
> @@ -281,53 +259,30 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
> /* Accept trailing fractional part. */
> && (*p == '\0' || *p == '.' || c_isspace(*p)))
> nodeinfo->mhz = ui;
> -# elif defined(__s390__) || \
> - defined(__s390x__)
> - } else if (STRPREFIX(buf, "# processors")) {
> - char *p;
> - unsigned int ui;
> - buf += 12;
> - while (*buf && c_isspace(*buf))
> - buf++;
> - if (*buf != ':' || !buf[1]) {
> - nodeReportError(VIR_ERR_INTERNAL_ERROR,
> - _("parsing number of processors %c"), *buf);
> - return -1;
> - }
> - if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0
> - && (*p == '\0' || c_isspace(*p)))
> - nodeinfo->cpus = ui;
> /* No other interesting infos are available in /proc/cpuinfo.
> * However, there is a line identifying processor's version,
> * identification and machine, but we don't want it to be caught
> * and parsed in next iteration, because it is not in expected
> * format and thus lead to error. */
> - break;
> # else
> # warning Parser for /proc/cpuinfo needs to be adapted for your architecture
> # endif
> }
> }
>
> - if (!nodeinfo->cpus) {
> - nodeReportError(VIR_ERR_INTERNAL_ERROR,
> - "%s", _("no cpus found"));
> - return -1;
> - }
> -
> - if (!need_hyperthreads)
> - return 0;
> -
> - /* OK, we've parsed what we can out of /proc/cpuinfo. Get the socket
> - * and thread information from /sys
> + /* OK, we've parsed clock speed out of /proc/cpuinfo. Get the core, socket
> + * thread and topology information from /sys
> */
> - remaining = nodeinfo->cpus;
> cpudir = opendir(CPU_SYS_PATH);
> if (cpudir == NULL) {
> virReportSystemError(errno, _("cannot opendir %s"), CPU_SYS_PATH);
> return -1;
> }
> - while ((errno = 0), remaining && (cpudirent = readdir(cpudir))) {
> +
> + CPU_ZERO(&core_mask);
> + CPU_ZERO(&socket_mask);
> +
> + while ((cpudirent = readdir(cpudir))) {
> if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1)
> continue;
>
> @@ -338,15 +293,19 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
> }
> if (!online)
> continue;
> - remaining--;
> + nodeinfo->cpus++;
>
> - socket = parse_socket(cpu);
> - if (socket < 0) {
> - closedir(cpudir);
> - return -1;
> + /* Parse core */
> + core = parse_core(cpu);
> + if (!CPU_ISSET(core, &core_mask)) {
> + CPU_SET(core, &core_mask);
> + nodeinfo->cores++;
> }
> - if (!(socket_mask & (1 << socket))) {
> - socket_mask |= (1 << socket);
> +
> + /* Parse socket */
> + socket = parse_socket(cpu);
> + if (!CPU_ISSET(socket, &socket_mask)) {
> + CPU_SET(socket, &socket_mask);
> nodeinfo->sockets++;
> }
>
> @@ -367,7 +326,12 @@ int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
>
> closedir(cpudir);
>
> - /* there should always be at least one socket and one thread */
> + /* there should always be at least one cpu, socket and one thread */
> + if (nodeinfo->cpus == 0) {
> + nodeReportError(VIR_ERR_INTERNAL_ERROR,
> + "%s", _("no CPUs found"));
> + return -1;
> + }
> if (nodeinfo->sockets == 0) {
> nodeReportError(VIR_ERR_INTERNAL_ERROR,
> "%s", _("no sockets found"));
> @@ -617,7 +581,7 @@ int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) {
> _("cannot open %s"), CPUINFO_PATH);
> return -1;
> }
> - ret = linuxNodeInfoCPUPopulate(cpuinfo, nodeinfo, true);
> + ret = linuxNodeInfoCPUPopulate(cpuinfo, nodeinfo);
> VIR_FORCE_FCLOSE(cpuinfo);
> if (ret < 0)
> return -1;
There's nothing particularly wrong with the idea here, but as Stefan
mentions this has broken the test suite, because the nodeinfotest.c
now tries to read /sys from the test machine, instead of reading
our dummy data files in tests/nodeinfodata/
I think we'd probably need to create some dummy sysfs trees under
tests/nodeinfodata/test1/sys/..../ and make sure we can pass in
a path to the linuxNodeInfoCPUPopulate API so we cna point the
test at our sysfs, instead of the real machine
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 :|
More information about the libvir-list
mailing list