]> xenbits.xensource.com Git - people/andrewcoop/hwloc.git/commitdiff
Xen topology support in hwloc
authorAndrew Cooper <andrew.cooper3@citrix.com>
Mon, 30 Dec 2013 20:51:25 +0000 (20:51 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 15 Jan 2014 18:05:55 +0000 (18:05 +0000)
This requires the following patch (which is applicable for backport to at
least Xen 4.3):

http://xenbits.xen.org/gitweb/?p=people/andrewcoop/xen.git;a=commitdiff;h=3585994405b6a73c137309dd4be91f48c71e4903

which implements a cleaner API for xc_{topology,numa}info.

This patch provides basic topology enumeration in hwloc.  Xen support should
be built automatically given an appropriate version of libxenctrl (i.e. with
the above patch) in the build environment.

Xen support can be built as a plugin (to remove direct dependences on
libxenctrl), and is disabled by default.  In dom0, use

`HWLOC_COMPONENETS=xen ./lstop ...`

to enable Xen system topology enumeration in preference to the faked topology
which the kernel gets to see.

This patch provides basic support for PUs, cores, sockets and numa nodes, with
numa nodes being annotated with availabe memory[1].  It has been sanity tested
on various different servers in our testing pool.

Some of this patch includes folded patches from Brice Goglin <Brice.Goglin@inria.fr>

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
[1] It turns out Xen's idea of memory attached to a node includes pages
mapping IO regions.  This is a hypervisor bug has been raised on xen-devel.

config/hwloc.m4
config/hwloc_internal.m4
src/Makefile.am
src/topology-xen.c [new file with mode: 0644]

index 38069335a58f70f6663b9c5892e6f327c8fd7fe3..75b07021f3aad4049e03a17c7aa7441c3bc26e4e 100644 (file)
@@ -1,6 +1,6 @@
 dnl -*- Autoconf -*-
 dnl
-dnl Copyright © 2009-2013 Inria.  All rights reserved.
+dnl Copyright © 2009-2014 Inria.  All rights reserved.
 dnl Copyright (c) 2009-2012 Université Bordeaux 1
 dnl Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 dnl                         University Research and Technology
@@ -914,6 +914,31 @@ EOF])
     fi
     # don't add LIBS/CFLAGS/REQUIRES yet, depends on plugins
 
+    # Xen support
+    hwloc_xen_happy=no
+    if test "x$enable_xen" != "xno"; then
+       hwloc_xen_happy=yes
+       AC_CHECK_HEADERS([xenctrl.h], [
+         AC_CHECK_LIB([xenctrl], [xc_topologyinfo_bounced], [HWLOC_XEN_LIBS="-lxenctrl"], [hwloc_xen_happy=no])
+        ], [hwloc_xen_happy=no])
+    fi
+    AC_SUBST(HWLOC_XEN_LIBS)
+    # If we asked for xen support but couldn't deliver, fail
+    AS_IF([test "$enable_xen" = "yes" -a "$hwloc_xen_happy" = "no"],
+         [AC_MSG_WARN([Specified --enable-xen switch, but could not])
+          AC_MSG_WARN([find appropriate support])
+          AC_MSG_ERROR([Cannot continue])])
+    if test "x$hwloc_xen_happy" = "xyes"; then
+      AC_DEFINE([HWLOC_HAVE_XEN], [1], [Define to 1 if you have the `XEN' library.])
+      AC_SUBST([HWLOC_HAVE_XEN], [1])
+      hwloc_components="$hwloc_components xen"
+      hwloc_xen_component_maybeplugin=1
+      hwloc_xen=yes
+    else
+      AC_SUBST([HWLOC_HAVE_XEN], [0])
+    fi
+    # don't add LIBS/CFLAGS/REQUIRES yet, depends on plugins
+
     # X11 support
     AC_PATH_XTRA
 
@@ -1127,6 +1152,10 @@ EOF])
           [HWLOC_LIBS="$HWLOC_LIBS $HWLOC_GL_LIBS"
            HWLOC_CFLAGS="$HWLOC_CFLAGS $HWLOC_GL_CFLAGS"
            HWLOC_REQUIRES="$HWLOC_GL_REQUIRES $HWLOC_REQUIRES"])
+    AS_IF([test "$hwloc_xen_component" = "static"],
+          [HWLOC_LIBS="$HWLOC_LIBS $HWLOC_XEN_LIBS"
+           HWLOC_CFLAGS="$HWLOC_CFLAGS $HWLOC_XEN_CFLAGS"
+           HWLOC_REQUIRES="$HWLOC_XEN_REQUIRES $HWLOC_REQUIRES"])
     AS_IF([test "$hwloc_xml_libxml_component" = "static"],
           [HWLOC_LIBS="$HWLOC_LIBS $HWLOC_LIBXML2_LIBS"
            HWLOC_CFLAGS="$HWLOC_CFLAGS $HWLOC_LIBXML2_CFLAGS"
@@ -1242,6 +1271,7 @@ AC_DEFUN([HWLOC_DO_AM_CONDITIONALS],[
         AM_CONDITIONAL([HWLOC_HAVE_X86_32], [test "x$hwloc_x86_32" = "xyes"])
         AM_CONDITIONAL([HWLOC_HAVE_X86_64], [test "x$hwloc_x86_64" = "xyes"])
         AM_CONDITIONAL([HWLOC_HAVE_CPUID], [test "x$hwloc_have_cpuid" = "xyes"])
+        AM_CONDITIONAL([HWLOC_HAVE_XEN], [test "x$hwloc_xen" = "xyes"])
 
         AM_CONDITIONAL([HWLOC_HAVE_PLUGINS], [test "x$hwloc_have_plugins" = "xyes"])
         AM_CONDITIONAL([HWLOC_PCI_BUILD_STATIC], [test "x$hwloc_pci_component" = "xstatic"])
@@ -1249,6 +1279,7 @@ AC_DEFUN([HWLOC_DO_AM_CONDITIONALS],[
         AM_CONDITIONAL([HWLOC_CUDA_BUILD_STATIC], [test "x$hwloc_cuda_component" = "xstatic"])
         AM_CONDITIONAL([HWLOC_NVML_BUILD_STATIC], [test "x$hwloc_nvml_component" = "xstatic"])
         AM_CONDITIONAL([HWLOC_GL_BUILD_STATIC], [test "x$hwloc_gl_component" = "xstatic"])
+        AM_CONDITIONAL([HWLOC_XEN_BUILD_STATIC], [test "x$hwloc_xen_component" = "xstatic"])
         AM_CONDITIONAL([HWLOC_XML_LIBXML_BUILD_STATIC], [test "x$hwloc_xml_libxml_component" = "xstatic"])
 
         AM_CONDITIONAL([HWLOC_HAVE_CXX], [test "x$hwloc_have_cxx" = "xyes"])
index bfc3f36284d199e88360c1eb5e0f545da0129671..13bb21d6db4c6afc1f89ac2e62d501e88289ae2b 100644 (file)
@@ -83,6 +83,11 @@ AC_DEFUN([HWLOC_DEFINE_ARGS],[
                   AS_HELP_STRING([--disable-nvml],
                                  [Disable the NVML device discovery]))
 
+    # Xen?
+    AC_ARG_ENABLE([xen],
+                  AS_HELP_STRING([--disable-xen],
+                                 [Disable Xen system discovery from dom0]))
+
     # GL/Display
     AC_ARG_ENABLE([gl],
                  AS_HELP_STRING([--disable-gl],
index 263895fd15ed5c14c9e813dae784d9258ac6de0a..69b80a0852ce9542e091267e6c82f28034a3dc6d 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright © 2009-2013 Inria.  All rights reserved.
+# Copyright © 2009-2014 Inria.  All rights reserved.
 # Copyright © 2009-2012 Université Bordeaux 1
 # Copyright © 2009-2010 Cisco Systems, Inc.  All rights reserved.
 # Copyright © 2011-2012 Oracle and/or its affiliates.  All rights reserved.
@@ -161,6 +161,17 @@ if HWLOC_HAVE_CPUID
 sources += topology-x86.c
 endif HWLOC_HAVE_CPUID
 
+if HWLOC_HAVE_XEN
+if HWLOC_XEN_BUILD_STATIC
+sources += topology-xen.c
+else
+plugins_LTLIBRARIES += hwloc_xen.la
+hwloc_xen_la_SOURCES = topology-xen.c
+hwloc_xen_la_CFLAGS = $(AM_CFLAGS) $(HWLOC_XEN_CFLAGS) -DHWLOC_INSIDE_PLUGIN
+hwloc_xen_la_LDFLAGS = $(plugins_ldflags) $(HWLOC_XEN_LIBS)
+endif
+endif HWLOC_HAVE_XEN
+
 if HWLOC_HAVE_GCC
 ldflags += -no-undefined
 endif HWLOC_HAVE_GCC
diff --git a/src/topology-xen.c b/src/topology-xen.c
new file mode 100644 (file)
index 0000000..bb0a995
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright © 2013-2014 Citrix Systems Ltd.
+ * Copyright © 2014 Inria.  All rights reserved.
+ * See COPYING in top-level directory.
+ */
+
+#include <private/autogen/config.h>
+#include <hwloc.h>
+#include <hwloc/plugins.h>
+
+/* private headers allowed for convenience because this plugin is built within hwloc */
+#include <private/debug.h>
+#include <private/misc.h>
+
+#include <inttypes.h>
+#include <assert.h>
+
+#include <xenctrl.h>
+
+
+/* Xen private data for hwloc_backend */
+struct hwloc_xen_priv {
+  xc_interface *xch;        /* Xenctrl handle */
+  xentoollog_logger logger; /* Xenctrl logger */
+};
+
+/* Topology and numa information from Xen */
+struct hwloc_xen_info {
+  uint32_t max_cpu_id;
+  uint32_t max_node_id;
+
+  /* From sysctl_topologyinfo */
+  uint32_t cpu_count;   /* Number of elements in of cpu_to_* arrays */
+  const uint32_t *cpu_to_core, *cpu_to_socket, *cpu_to_node;
+
+  /* From sysctl_numainfo */
+  uint32_t node_count;   /* Number of elements in of node_to_* arrays */
+  const uint64_t *node_to_memsize, *node_to_memfree;
+  const uint32_t *node_to_node_distance; /* (node_count * node_count) elements */
+};
+
+/* Xenctrl Logging function */
+static void
+xenctl_logfn(struct xentoollog_logger *logger __hwloc_attribute_unused,
+             xentoollog_level level __hwloc_attribute_unused,
+             int errnoval __hwloc_attribute_unused,
+             const char *context __hwloc_attribute_unused,
+             const char *format __hwloc_attribute_unused,
+             va_list al __hwloc_attribute_unused)
+{
+#ifdef HWLOC_DEBUG
+  /* Dont bother snprintf()ing the buffer if we will throw it away anyway. */
+  static char logbuf[512];
+
+  vsnprintf(logbuf, sizeof logbuf, format, al);
+  hwloc_debug("%s: %s: %s\n", context, xtl_level_to_string(level), logbuf);
+#endif
+}
+
+/* Allocate a hwloc_xen_info structure */
+static struct hwloc_xen_info *
+alloc_xen_info(void)
+{
+  struct hwloc_xen_info *p = malloc(sizeof *p);
+
+  if (p)
+    memset(p, 0, sizeof *p);
+
+  return p;
+}
+
+/* Free a hwloc_xen_info structure */
+static void
+free_xen_info(struct hwloc_xen_info *p)
+{
+  if (p) {
+    free((void *)p->node_to_node_distance);
+    free((void *)p->node_to_memfree);
+    free((void *)p->node_to_memsize);
+    free((void *)p->cpu_to_node);
+    free((void *)p->cpu_to_socket);
+    free((void *)p->cpu_to_core);
+    free(p);
+  }
+}
+
+/* Perform hypercalls to query Xen for information, and fill in a
+ * hwloc_xen_info structure. */
+static struct hwloc_xen_info *
+hwloc_get_xen_info(xc_interface *xch)
+{
+  struct hwloc_xen_info *data = alloc_xen_info();
+  xc_physinfo_t physinfo;
+
+  if (!data)
+    return NULL;
+
+  /* Ask Xen for the physical information.  This provides the sizes of arrays
+   * required to query the topology and numa information. */
+  if (xc_physinfo(xch, &physinfo))
+    goto out;
+
+  { /* Ask for the full topology information */
+    uint32_t *cpu_to_core, *cpu_to_socket, *cpu_to_node;
+
+    data->max_cpu_id = physinfo.max_cpu_id;
+    data->cpu_count = data->max_cpu_id + 1;
+
+    cpu_to_core   = calloc(data->cpu_count, sizeof *cpu_to_core);
+    cpu_to_socket = calloc(data->cpu_count, sizeof *cpu_to_socket);
+    cpu_to_node   = calloc(data->cpu_count, sizeof *cpu_to_node);
+
+    if (!cpu_to_core || !cpu_to_socket || !cpu_to_node)
+      goto out;
+
+    if (xc_topologyinfo_bounced(xch, &data->max_cpu_id,
+                                cpu_to_core, cpu_to_socket, cpu_to_node))
+      goto out;
+
+    assert((data->max_cpu_id < data->cpu_count) &&
+           "Xen overflowed our arrays");
+
+    data->cpu_to_core = cpu_to_core;
+    data->cpu_to_socket = cpu_to_socket;
+    data->cpu_to_node = cpu_to_node;
+  }
+
+  { /* Ask for the full numa memory information */
+    uint64_t *node_to_memsize, *node_to_memfree;
+    uint32_t *node_to_node_distance;
+
+    data->max_node_id = physinfo.max_node_id;
+    data->node_count = data->max_node_id + 1;
+
+    node_to_memsize = calloc(data->node_count, sizeof *node_to_memsize);
+    node_to_memfree = calloc(data->node_count, sizeof *node_to_memfree);
+    node_to_node_distance = calloc(data->node_count * data->node_count,
+                                   sizeof *node_to_node_distance);
+
+    if (!node_to_memsize || !node_to_memfree || !node_to_node_distance)
+      goto out;
+
+    if (xc_numainfo_bounced(xch, &data->max_node_id,
+                            node_to_memsize, node_to_memfree,
+                            node_to_node_distance))
+      goto out;
+
+    assert((data->max_node_id < data->node_count) &&
+           "Xen overflowed our arrays");
+
+    data->node_to_memsize = node_to_memsize;
+    data->node_to_memfree = node_to_memfree;
+    data->node_to_node_distance = node_to_node_distance;
+  }
+
+  return data;
+
+ out:
+  free_xen_info(data);
+  return NULL;
+}
+
+/* Enumerate the full Xen system and fill in appropriate objects into the
+ * topology. */
+static int
+hwloc_xen_discover(struct hwloc_backend *backend)
+{
+  struct hwloc_topology *topology = backend->topology;
+  struct hwloc_xen_priv *priv = backend->private_data;
+  struct hwloc_xen_info *data;
+  hwloc_bitmap_t each_socket, each_node, each_core;
+  uint32_t i, j, z;
+
+  if (hwloc_get_root_obj(topology)->cpuset)
+    return 0;
+
+  hwloc_debug("Discovering Xen topology\n");
+
+  data = hwloc_get_xen_info(priv->xch);
+  assert(data && "Failed to gather data from Xen\n");
+
+  hwloc_debug("Xen topology information\n");
+  hwloc_debug("  cpu count %"PRIu32", max cpu id %"PRIu32"\n",
+              data->cpu_count, data->max_cpu_id);
+
+  for ( z = 0; z <= data->max_cpu_id; ++z )
+    hwloc_debug("  cpu[%3"PRIu32"], core %3"PRIu32", sock %3"PRIu32", node %3"PRIu32"\n",
+                z, data->cpu_to_core[z], data->cpu_to_socket[z],
+                data->cpu_to_node[z]);
+
+  hwloc_debug("Xen NUMA information\n");
+  hwloc_debug("  numa count %"PRIu32", max numa id %"PRIu32"\n",
+              data->node_count, data->max_node_id);
+  for ( z = 0; z <= data->max_node_id; ++z )
+    hwloc_debug("  node[%3"PRIu32"], size %"PRIu64", free %"PRIu64"\n",
+                z, data->node_to_memsize[z], data->node_to_memfree[z]);
+
+  hwloc_alloc_root_sets(topology);
+  hwloc_setup_pu_level(topology, data->max_cpu_id+1);
+
+  /* Socket information */
+  each_socket = hwloc_bitmap_alloc();
+  for ( z = 0; z <= data->max_cpu_id; ++z )
+    if (data->cpu_to_socket[z] != ~0U)
+      hwloc_bitmap_set(each_socket, data->cpu_to_socket[z]);
+
+  hwloc_debug_bitmap("Xen each_sockets is %s\n", each_socket);
+
+  each_core = hwloc_bitmap_alloc();
+  hwloc_bitmap_foreach_begin(i, each_socket)
+    {
+      struct hwloc_obj *sock =
+        hwloc_alloc_setup_object(HWLOC_OBJ_SOCKET, i);
+      sock->cpuset = hwloc_bitmap_alloc();
+
+      for ( z = 0; z <= data->max_cpu_id; ++z )
+        if (data->cpu_to_socket[z] == i)
+          hwloc_bitmap_set(sock->cpuset, z);
+
+      hwloc_insert_object_by_cpuset(topology, sock);
+      hwloc_debug_1arg_bitmap(" Xen cpus on socket %u are %s\n", i, sock->cpuset);
+
+      /* Core information (Core IDs are enumerated per-socket in Xen ) */
+      hwloc_bitmap_zero(each_core);
+      for ( z = 0; z <= data->max_cpu_id; ++z )
+        if (data->cpu_to_socket[z] == i &&
+            data->cpu_to_core[z] != ~0U )
+          hwloc_bitmap_set(each_core, data->cpu_to_core[z]);
+      hwloc_debug_bitmap("  Xen each_core is %s\n", each_core);
+
+      hwloc_bitmap_foreach_begin(j, each_core)
+        {
+          struct hwloc_obj *core =
+            hwloc_alloc_setup_object(HWLOC_OBJ_CORE, j);
+          core->cpuset = hwloc_bitmap_alloc();
+
+          for ( z = 0; z <= data->max_cpu_id; ++z )
+            if (data->cpu_to_socket[z] == i &&
+                data->cpu_to_core[z] == j)
+              hwloc_bitmap_set(core->cpuset, z);
+
+          hwloc_insert_object_by_cpuset(topology, core);
+          hwloc_debug_1arg_bitmap("   Xen cpus on core %u are %s\n", j, core->cpuset);
+        }
+      hwloc_bitmap_foreach_end();
+    }
+  hwloc_bitmap_foreach_end();
+  hwloc_bitmap_free(each_core);
+  hwloc_bitmap_free(each_socket);
+
+  /* Node information */
+  each_node = hwloc_bitmap_alloc();
+  for ( z = 0; z <= data->max_cpu_id; ++z )
+    if (data->cpu_to_node[z] != ~0U)
+      hwloc_bitmap_set(each_node, data->cpu_to_node[z]);
+
+  hwloc_debug_bitmap("Xen each_node is %s\n", each_node);
+
+  hwloc_bitmap_foreach_begin(i, each_node)
+    {
+      struct hwloc_obj *node =
+        hwloc_alloc_setup_object(HWLOC_OBJ_NODE, i);
+      node->cpuset = hwloc_bitmap_alloc();
+
+      for ( z = 0; z <= data->max_cpu_id; ++z )
+        if (data->cpu_to_node[z] == i)
+          hwloc_bitmap_set(node->cpuset, z);
+
+      /* Fill in some memory information, as we have it to hand.
+       * Xen doesn't really do superpages yet. */
+      if (data->node_to_memsize[i] != ~0U) {
+        node->memory.page_types_len = 1;
+        node->memory.page_types = calloc(1, sizeof *node->memory.page_types);
+
+        node->memory.page_types[0].size = 4096;
+        node->memory.page_types[0].count = data->node_to_memsize[i] >> 12;
+        node->memory.local_memory = data->node_to_memsize[i];
+      }
+
+      hwloc_insert_object_by_cpuset(topology, node);
+
+      hwloc_debug_1arg_bitmap(" Xen cpus on node %u are %s\n", i, node->cpuset);
+    }
+  hwloc_bitmap_foreach_end();
+  hwloc_bitmap_free(each_node);
+
+
+  hwloc_debug("All done discovering Xen topology\n\n");
+  hwloc_obj_add_info(hwloc_get_root_obj(topology), "Backend", "Xen");
+
+  free_xen_info(data);
+
+  return 1;
+}
+
+/* Free the backend private data */
+static void
+hwloc_xen_backend_disable(struct hwloc_backend *backend)
+{
+  struct hwloc_xen_priv *priv = backend->private_data;
+
+  if (priv) {
+
+    if (priv->xch)
+      xc_interface_close(priv->xch);
+    free(priv);
+
+    backend->private_data = NULL;
+  }
+}
+
+/* Try to set up Xen topology enumeration */
+static struct hwloc_backend *
+hwloc_xen_component_instantiate(struct hwloc_disc_component *component,
+                                const void *_data1 __hwloc_attribute_unused,
+                                const void *_data2 __hwloc_attribute_unused,
+                                const void *_data3 __hwloc_attribute_unused)
+{
+  struct hwloc_backend *backend = NULL;
+  struct hwloc_xen_priv *priv = NULL;
+
+  backend = hwloc_backend_alloc(component);
+  if (!backend)
+    goto err;
+
+  priv = malloc(sizeof *priv);
+  if (!priv)
+    goto err;
+
+  priv->logger.vmessage = xenctl_logfn;
+
+  /* This will fail if we are not running as root in dom0. */
+  priv->xch = xc_interface_open(&priv->logger, &priv->logger, 0);
+  if (!priv->xch) {
+    hwloc_debug("xc_interface_open() failed. Are you running as root in dom0?"
+                " Disabling xen component.\n");
+    goto err;
+  }
+
+  backend->private_data = priv;
+  backend->discover = hwloc_xen_discover;
+  backend->disable = hwloc_xen_backend_disable;
+
+  return backend;
+
+ err:
+  if (priv && priv->xch)
+    xc_interface_close(priv->xch);
+  free(priv);
+  free(backend);
+  return NULL;
+}
+
+static struct hwloc_disc_component hwloc_xen_disc_component = {
+  HWLOC_DISC_COMPONENT_TYPE_CPU,
+  "xen",
+
+  /* If Xen system support works, we can be absolutely certain that all other
+   * CPU discovery methods will be wrong. */
+  HWLOC_DISC_COMPONENT_TYPE_CPU | HWLOC_DISC_COMPONENT_TYPE_GLOBAL,
+  hwloc_xen_component_instantiate,
+  0, /* Should explicitly be requested with HWLOC_COMPONENTS=xen,... */
+  NULL
+};
+
+#ifdef HWLOC_INSIDE_PLUGIN
+HWLOC_DECLSPEC extern const struct hwloc_component hwloc_xen_component;
+#endif
+
+const struct hwloc_component hwloc_xen_component = {
+  HWLOC_COMPONENT_ABI,
+  HWLOC_COMPONENT_TYPE_DISC,
+  0,
+  &hwloc_xen_disc_component
+};