eal: rework cpu information discovery
authorIntel <intel.com>
Wed, 19 Dec 2012 23:00:00 +0000 (00:00 +0100)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Thu, 25 Jul 2013 13:23:26 +0000 (15:23 +0200)
Signed-off-by: Intel
lib/librte_eal/common/include/rte_lcore.h
lib/librte_eal/linuxapp/eal/eal_lcore.c
lib/librte_eal/linuxapp/eal/include/exec-env/rte_lcore.h
tools/cpu_layout.py [new file with mode: 0755]

index 9838353..b8f4b7c 100644 (file)
@@ -92,15 +92,17 @@ rte_lcore_count(void)
 
 #include <exec-env/rte_lcore.h>
 
-#ifdef __DOXYGEN__
 /**
  * Return the ID of the physical socket of the logical core we are
  * running on.
  * @return
- *   Socket ID
+ *   the ID of current lcoreid's physical socket
  */
 static inline unsigned
-rte_socket_id(void);
+rte_socket_id(void)
+{
+       return lcore_config[rte_lcore_id()].socket_id;
+}
 
 /**
  * Get the ID of the physical socket of the specified lcore
@@ -111,10 +113,10 @@ rte_socket_id(void);
  *   the ID of lcoreid's physical socket
  */
 static inline unsigned
-rte_lcore_to_socket_id(unsigned lcore_id);
-
-#endif
-
+rte_lcore_to_socket_id(unsigned lcore_id)
+{
+       return lcore_config[lcore_id].socket_id;
+}
 
 /**
  * Test if an lcore is enabled.
index 3fe7178..c4e6492 100644 (file)
  * 
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <inttypes.h>
+#include <unistd.h>
+#include <limits.h>
 #include <string.h>
-#include <errno.h>
-#include <sys/queue.h>
+#include <dirent.h>
 
 #include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_launch.h>
-#include <rte_tailq.h>
 #include <rte_eal.h>
-#include <rte_per_lcore.h>
 #include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
 #include <rte_debug.h>
 
 #include "eal_private.h"
+#include "eal_filesystem.h"
 
-#define PROC_CPUINFO "/proc/cpuinfo"
-#define PROC_PROCESSOR_FMT ""
+#define SYS_CPU_DIR "/sys/devices/system/cpu/cpu%u"
+#define CORE_ID_FILE "topology/core_id"
+#define PHYS_PKG_FILE "topology/physical_package_id"
 
-/* parse one line and try to match "processor : %d". */
+/* Check if a cpu is present by the presence of the cpu information for it */
 static int
-parse_processor_id(const char *buf, unsigned *lcore_id)
+cpu_detected(unsigned lcore_id)
 {
-       static const char _processor[] = "processor";
-       const char *s;
-
-       if (strncmp(buf, _processor, sizeof(_processor) - 1) != 0)
-               return -1;
+       char path[PATH_MAX];
+       int len = rte_snprintf(path, sizeof(path), SYS_CPU_DIR
+               "/"CORE_ID_FILE, lcore_id);
+       if (len <= 0 || (unsigned)len >= sizeof(path))
+               return 0;
+       if (access(path, F_OK) != 0)
+               return 0;
+
+       return 1;
+}
 
-       s = strchr(buf, ':');
-       if (s == NULL)
-               return -1;
+/* Get CPU socket id (NUMA node) by reading directory
+ * /sys/devices/system/cpu/cpuX looking for symlink "nodeY"
+ * which gives the NUMA topology information.
+ * Note: physical package id != NUMA node, but we use it as a
+ * fallback for kernels which don't create a nodeY link
+ */
+static unsigned
+cpu_socket_id(unsigned lcore_id)
+{
+       const char node_prefix[] = "node";
+       const size_t prefix_len = sizeof(node_prefix) - 1;
+       char path[PATH_MAX];
+       DIR *d;
+       unsigned long id = 0;
+       struct dirent *e;
+       char *endptr = NULL;
+
+       int len = rte_snprintf(path, sizeof(path),
+                              SYS_CPU_DIR, lcore_id);
+       if (len <= 0 || (unsigned)len >= sizeof(path))
+               goto err;
+
+       d = opendir(path);
+       if (!d)
+               goto err;
+
+       while ((e = readdir(d)) != NULL) {
+               if (strncmp(e->d_name, node_prefix, prefix_len) == 0) {
+                       id = strtoul(e->d_name+prefix_len, &endptr, 0);
+                       break;
+               }
+       }
+       closedir(d);
+       if (endptr == NULL || *endptr!='\0' || endptr == e->d_name+prefix_len) {
+               RTE_LOG(ERR, EAL, "Error reading numa node link "
+                               "for lcore %u - using physical package id instead\n",
+                               lcore_id);
 
-       errno = 0;
-       *lcore_id = strtoul(s+1, NULL, 10);
-       if (errno != 0) {
-               *lcore_id = -1;
-               return -1;
+               len = rte_snprintf(path, sizeof(path), SYS_CPU_DIR "/%s",
+                               lcore_id, PHYS_PKG_FILE);
+               if (len <= 0 || (unsigned)len >= sizeof(path))
+                       goto err;
+               if (eal_parse_sysfs_value(path, &id) != 0)
+                       goto err;
        }
+       return (unsigned)id;
 
+err:
+       RTE_LOG(ERR, EAL, "Error getting NUMA socket information from %s "
+                       "for lcore %u - assuming NUMA socket 0\n", SYS_CPU_DIR, lcore_id);
        return 0;
 }
 
-/* parse one line and try to match "physical id : %d". */
-static int
-parse_socket_id(const char *buf, unsigned *socket_id)
+/* Get the cpu core id value from the /sys/.../cpuX core_id value */
+static unsigned
+cpu_core_id(unsigned lcore_id)
 {
-       static const char _physical_id[] = "physical id";
-       const char *s;
-
-       if (strncmp(buf, _physical_id, sizeof(_physical_id) - 1) != 0)
-               return -1;
-
-       s = strchr(buf, ':');
-       if (s == NULL)
-               return -1;
-
-       errno = 0;
-       *socket_id = strtoul(s+1, NULL, 10);
-       if (errno != 0)
-               return -1;
-
+       char path[PATH_MAX];
+       unsigned long id;
+
+       int len = rte_snprintf(path, sizeof(path), SYS_CPU_DIR "/%s", lcore_id, CORE_ID_FILE);
+       if (len <= 0 || (unsigned)len >= sizeof(path))
+               goto err;
+       if (eal_parse_sysfs_value(path, &id) != 0)
+               goto err;
+       return (unsigned)id;
+
+err:
+       RTE_LOG(ERR, EAL, "Error reading core id value from %s "
+                       "for lcore %u - assuming core 0\n", SYS_CPU_DIR, lcore_id);
        return 0;
 }
 
 /*
- * Parse /proc/cpuinfo to get the number of physical and logical
+ * Parse /sys/devices/system/cpu to get the number of physical and logical
  * processors on the machine. The function will fill the cpu_info
  * structure.
  */
 int
 rte_eal_cpu_init(void)
 {
-       struct rte_config *config;
-       FILE *f;
-       char buf[BUFSIZ];
-       unsigned lcore_id = 0;
-       unsigned socket_id = 0;
+       /* pointer to global configuration */
+       struct rte_config *config = rte_eal_get_configuration();
+       unsigned lcore_id;
        unsigned count = 0;
 
-       /* get pointer to global configuration */
-       config = rte_eal_get_configuration();
-
-       /* open /proc/cpuinfo */
-       f = fopen(PROC_CPUINFO, "r");
-       if (f == NULL) {
-               RTE_LOG(ERR, EAL, "%s(): Cannot find "PROC_CPUINFO"\n", __func__);
-               return -1;
-       }
-
-       /*
-        * browse lines of /proc/cpuinfo and fill memseg entries in
-        * global configuration
-        */
-       while (fgets(buf, sizeof(buf), f) != NULL) {
-
-               if (parse_processor_id(buf, &lcore_id) == 0)
-                       continue;
-
-               if (parse_socket_id(buf, &socket_id) == 0)
-                       continue;
-
-               if (buf[0] == '\n') {
-                       RTE_LOG(DEBUG, EAL, "Detected lcore %u on socket %u\n",
-                               lcore_id, socket_id);
-                       if (lcore_id >= RTE_MAX_LCORE) {
-                               RTE_LOG(DEBUG, EAL,
-                                       "Skip lcore %u >= RTE_MAX_LCORE\n",
-                                         lcore_id);
-                               continue;
-                       }
-
-                       /*
-                        * In a virtualization environment, the socket ID
-                        * reported by the system may not be linked to a real
-                        * physical socket ID, and may be incoherent. So in this
-                        * case, a default socket ID of 0 is assigned.
-                        */
-                       if (socket_id >= RTE_MAX_NUMA_NODES) {
-#ifdef CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID
-                               socket_id = 0;
-#else
-                               rte_panic("Socket ID (%u) is greater than "
-                                   "RTE_MAX_NUMA_NODES (%d)\n",
-                                   socket_id, RTE_MAX_NUMA_NODES);
-#endif
-                       }
-
-                       lcore_config[lcore_id].detected = 1;
-                       lcore_config[lcore_id].socket_id = socket_id;
-
-               }
-       }
-
-       fclose(f);
-
        /* disable lcores that were not detected */
        RTE_LCORE_FOREACH(lcore_id) {
 
+               lcore_config[lcore_id].detected = cpu_detected(lcore_id);
                if (lcore_config[lcore_id].detected == 0) {
-                       RTE_LOG(DEBUG, EAL, "Skip lcore %u (not detected)\n",
-                               lcore_id);
+                       RTE_LOG(DEBUG, EAL, "Skip lcore %u (not detected)\n", lcore_id);
                        config->lcore_role[lcore_id] = ROLE_OFF;
+                       continue;
                }
-               else
-                       count ++;
+               lcore_config[lcore_id].core_id = cpu_core_id(lcore_id);
+               lcore_config[lcore_id].socket_id = cpu_socket_id(lcore_id);
+               if (lcore_config[lcore_id].socket_id >= RTE_MAX_NUMA_NODES)
+#ifdef CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID
+                       lcore_config[lcore_id].socket_id = 0;
+#else
+                       rte_panic("Socket ID (%u) is greater than "
+                               "RTE_MAX_NUMA_NODES (%d)\n",
+                               lcore_config[lcore_id].socket_id, RTE_MAX_NUMA_NODES);
+#endif
+               RTE_LOG(DEBUG, EAL, "Detected lcore %u as core %u on socket %u\n",
+                               lcore_id,
+                               lcore_config[lcore_id].core_id,
+                               lcore_config[lcore_id].socket_id);
+               count ++;
        }
 
        config->lcore_count = count;
index 4754006..c0bf8e4 100644 (file)
@@ -57,6 +57,7 @@ struct lcore_config {
        volatile int ret;          /**< return value of function */
        volatile enum rte_lcore_state_t state; /**< lcore state */
        unsigned socket_id;        /**< physical socket id for this lcore */
+       unsigned core_id;          /**< core number on socket for this lcore */
 };
 
 /**
@@ -64,28 +65,4 @@ struct lcore_config {
  */
 extern struct lcore_config lcore_config[RTE_MAX_LCORE];
 
-/**
- * Return the ID of the physical socket of the logical core we are
- * running on.
- */
-static inline unsigned
-rte_socket_id(void)
-{
-       return lcore_config[rte_lcore_id()].socket_id;
-}
-
-/**
- * Get the ID of the physical socket of the specified lcore
- *
- * @param lcore_id
- *   the targeted lcore, which MUST be between 0 and RTE_MAX_LCORE-1.
- * @return
- *   the ID of lcoreid's physical socket
- */
-static inline unsigned
-rte_lcore_to_socket_id(unsigned lcore_id)
-{
-       return lcore_config[lcore_id].socket_id;
-}
-
-#endif /* _RTE_LCORE_H_ */
+#endif /* _RTE_LINUXAPP_LCORE_H_ */
diff --git a/tools/cpu_layout.py b/tools/cpu_layout.py
new file mode 100755 (executable)
index 0000000..ea19214
--- /dev/null
@@ -0,0 +1,90 @@
+#! /usr/bin/python
+#
+#   BSD LICENSE
+# 
+#   Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
+#   All rights reserved.
+# 
+#   Redistribution and use in source and binary forms, with or without 
+#   modification, are permitted provided that the following conditions 
+#   are met:
+# 
+#     * Redistributions of source code must retain the above copyright 
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright 
+#       notice, this list of conditions and the following disclaimer in 
+#       the documentation and/or other materials provided with the 
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its 
+#       contributors may be used to endorse or promote products derived 
+#       from this software without specific prior written permission.
+# 
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+#
+
+import sys
+
+sockets = []
+cores = []
+core_map = {}
+
+fd=open("/proc/cpuinfo")
+lines = fd.readlines()
+fd.close()
+
+core_details = []
+core_lines = {}
+for line in lines:
+       if len(line.strip()) != 0:
+               name, value = line.split(":", 1)
+               core_lines[name.strip()] = value.strip()
+       else:
+               core_details.append(core_lines)
+               core_lines = {}
+
+for core in core_details:
+       for field in ["processor", "core id", "physical id"]:
+               if field not in core:
+                       print "Error getting '%s' value from /proc/cpuinfo" % field
+                       sys.exit(1)
+               core[field] = int(core[field])
+
+       if core["core id"] not in cores:
+               cores.append(core["core id"])
+       if core["physical id"] not in sockets:
+               sockets.append(core["physical id"])
+       key = (core["physical id"], core["core id"])
+       if key not in core_map:
+               core_map[key] = []
+       core_map[key].append(core["processor"])
+
+print "============================================================"
+print "Core and Socket Information (as reported by '/proc/cpuinfo')"
+print "============================================================\n"
+print "cores = ",cores
+print "sockets = ", sockets
+print ""
+
+for s in sockets:
+       print "\tSocket %s" % s,
+print ""
+for s in sockets:
+       print "\t---------",
+print ""
+
+for c in cores:
+       print "Core %s" % c,
+       for s in sockets:
+               print "\t", core_map[(s,c)],
+       print "\n"