]> 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>
Thu, 13 Feb 2014 01:32:13 +0000 (01:32 +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=c1faa00881ba1de42e3aff6a7009a69ff7940b19

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.

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

index 95bf610e98076a424ab735a57380e68915db33c9..985ce1f537f8ea10298ebeecf31a0417b4166d54 100644 (file)
@@ -52,7 +52,7 @@ test-suite.log
 
 /src/static-components.h
 /src/*.lo
-/src/libhwloc.la
+/src/*.la
 /src/libltdl/
 
 /tests/wrapper.sh
index bfee6e11d0399221406ccd3071784fd91b42306f..e17389f5145cf284b8539e47e7eefd03095378fb 100644 (file)
@@ -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
 
@@ -1142,6 +1167,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"
@@ -1257,6 +1286,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"])
@@ -1264,6 +1294,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 ab03f70a025811f76a26ac873fcefaafc2438d46..e81e2ca70f3804101dcfb341bbaca894cbf560d2 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
+};