Provide Linux-specific discovery routines. The discovery reads the
/sys/bus/platform/devices/*/uevent file for each device. If the uevent
file contains OF_FULLNAME entry (path in the Device Tree) and a list of
OF_COMPATIBLE_# entries, it is considered as a SoC device and inserted
into the soc_device_list.

We do not care about the mem_resources at the moment. We need a proper
Linux Kernel driver to support this. Gathering of the resource information
could be done by parsing reg properties in the Device Tree.

There is a possible pitfall here, if there is a device depending on another
device (eg. an EMAC with a separate DMA engine), we cannot treat it as a
single device as the relation between whose might not be described in a
standardized way. So the drivers of the particular devices must take care of
this themselfs.

Signed-off-by: Jan Viktorin <viktorin at rehivetech.com>
---
 lib/librte_eal/linuxapp/eal/Makefile  |   2 +
 lib/librte_eal/linuxapp/eal/eal_soc.c | 394 ++++++++++++++++++++++++++++++++++
 2 files changed, 396 insertions(+)
 create mode 100644 lib/librte_eal/linuxapp/eal/eal_soc.c

diff --git a/lib/librte_eal/linuxapp/eal/Makefile 
b/lib/librte_eal/linuxapp/eal/Makefile
index f2ed696..11e2dc8 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -60,6 +60,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_pci.c
 SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_pci_uio.c
 SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_pci_vfio.c
 SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_pci_vfio_mp_sync.c
+SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_soc.c
 SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_debug.c
 SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_lcore.c
 SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_timer.c
@@ -105,6 +106,7 @@ CFLAGS_eal_hugepage_info.o := -D_GNU_SOURCE
 CFLAGS_eal_pci.o := -D_GNU_SOURCE
 CFLAGS_eal_pci_uio.o := -D_GNU_SOURCE
 CFLAGS_eal_pci_vfio.o := -D_GNU_SOURCE
+CFLAGS_eal_soc.o := -D_GNU_SOURCE
 CFLAGS_eal_common_whitelist.o := -D_GNU_SOURCE
 CFLAGS_eal_common_options.o := -D_GNU_SOURCE
 CFLAGS_eal_common_thread.o := -D_GNU_SOURCE
diff --git a/lib/librte_eal/linuxapp/eal/eal_soc.c 
b/lib/librte_eal/linuxapp/eal/eal_soc.c
new file mode 100644
index 0000000..be0e44d
--- /dev/null
+++ b/lib/librte_eal/linuxapp/eal/eal_soc.c
@@ -0,0 +1,394 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 RehiveTech. 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.
+ */
+
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <rte_log.h>
+#include <rte_pci.h>
+#include <rte_eal_memconfig.h>
+#include <rte_malloc.h>
+#include <rte_devargs.h>
+#include <rte_memcpy.h>
+
+#include "eal_filesystem.h"
+#include "eal_private.h"
+#include "eal_pci_init.h"
+
+int
+soc_map_device(struct rte_soc_device *dev)
+{
+       int rc = -1;
+
+       switch (dev->kdrv) {
+       case RTE_SOC_KDRV_NONE:
+               rc = 0;
+               break;
+       default:
+               RTE_LOG(DEBUG, EAL,
+                       "  Not managed by a supported kernel driver, 
skipped\n");
+               rc = 1;
+               break;
+       }
+
+       return rc;
+}
+
+void
+soc_unmap_device(struct rte_soc_device *dev)
+{
+       switch (dev->kdrv) {
+       case RTE_SOC_KDRV_NONE:
+               break;
+       default:
+               RTE_LOG(DEBUG, EAL,
+                       "  Not managed by a supported kernel driver, 
skipped\n");
+               break;
+       }
+}
+
+static char *
+linecpy(char *dst, const char *line, size_t max)
+{
+       size_t len = 0;
+
+       while (line[len] && line[len] != '\n')
+               len += 1;
+
+       return (char *) memcpy(dst, line, len > max? max : len);
+}
+
+static char *
+linedup(const char *line)
+{
+       size_t len = 0;
+       char *s;
+
+       while (line[len] && line[len] != '\n')
+               len += 1;
+
+       s = malloc(len + 1);
+       if (s == NULL)
+               return NULL;
+
+       memcpy(s, line, len);
+       s[len] = '\0';
+       return s;
+}
+
+static const char *
+uevent_find_entry(const char *start, const char *end,
+               const char *prefix, const char *context)
+{
+       const size_t len = strlen(prefix);
+       const char *p = start;
+
+       while (strncmp(prefix, p, len)) {
+               while (p < end && *p != '\n') {
+                       p += 1;
+               }
+
+               if (p >= end) {
+                       RTE_LOG(WARNING, EAL,
+                               "%s(): missing uevent entry %s (%s)\n",
+                               __func__, prefix, context);
+                       return NULL;
+               }
+               else {
+                       p += 1; /* skip end-of-line */
+               }
+       }
+
+       if (p + len < end)
+               return p + len;
+       else {
+               RTE_LOG(WARNING, EAL,
+                       "%s(): missing value for uevent entry %s (%s)\n",
+                       __func__, prefix, context);
+               return NULL;
+       }
+}
+
+static int
+soc_device_from_uevent(struct rte_soc_device *dev, const char *uevent)
+{
+       FILE *f;
+       struct stat st;
+       char *buf;
+       char *end;
+       char *err;
+       const char *entry;
+       unsigned long i;
+       unsigned long count;
+
+       if ((f = fopen(uevent, "r")) == NULL) {
+               RTE_LOG(ERR, EAL, "%s(): cannot open sysfs file uevent\n",
+                       __func__);
+               return -1;
+       }
+
+       if (fstat(fileno(f), &st) < 0) {
+               RTE_LOG(ERR, EAL, "%s(): cannot stat sysfs file uevent (%s)\n",
+                       __func__, strerror(errno));
+               goto fail_fclose;
+       }
+
+       if (st.st_size <= 0) {
+               RTE_LOG(ERR, EAL, "%s(): sysfs file uevent seems to be empty\n",
+                       __func__);
+               goto fail_fclose_skip;
+       }
+
+       buf = malloc(st.st_size + 1);
+       if (buf == NULL) {
+               RTE_LOG(ERR, EAL, "%s(): failed to alloc memory\n", __func__);
+               goto fail_fclose;
+       }
+
+       if (fread(buf, 1, st.st_size, f) == 0) {
+               RTE_LOG(ERR, EAL, "%s(): cannot read sysfs file uevent\n",
+                       __func__);
+               goto fail_free_buf;
+       }
+       buf[st.st_size] = '\0';
+       end = buf + st.st_size;
+
+       entry = uevent_find_entry(buf, end, "OF_FULLNAME=", uevent);
+       if (entry == NULL)
+               goto fail_free_buf_skip;
+
+       linecpy((char *) dev->addr.devtree_path, entry,
+               sizeof(dev->addr.devtree_path));
+
+       RTE_LOG(DEBUG, EAL, "%s(): OF_FULLNAME=%s\n", __func__,
+                       dev->addr.devtree_path);
+
+       entry = uevent_find_entry(buf, end, "OF_COMPATIBLE_N=", uevent);
+       if (entry == NULL)
+               goto fail_free_buf_skip; /* reported from uevent_find_entry */
+
+       count = strtoul(entry, &err, 0);
+       if (err == NULL) {
+               RTE_LOG(ERR, EAL, "%s(): failed to parse OF_COMPATIBLE_N\n",
+                       __func__);
+               goto fail_free_buf;
+       }
+
+       RTE_LOG(DEBUG, EAL, "%s(): OF_COMPATIBLE_N=%lu\n", __func__, count);
+
+       dev->id.compatible = calloc(count + 1, sizeof(*dev->id.compatible));
+       if (dev->id.compatible == NULL) {
+               RTE_LOG(ERR, EAL, "%s(): failed to alloc memory\n",
+                       __func__);
+               goto fail_free_buf;
+       }
+
+       if (count > 9999) /* FIXME: better way? */
+               rte_exit(EXIT_FAILURE, "Strange count of OF_COMPATIBLE entries"
+                               "in sysfs uevent\n");
+
+       for (i = 0; i < count; ++i) {
+               char prefix[strlen("OF_COMPATIBLE_NNNN=")];
+               snprintf(prefix, sizeof(prefix), "OF_COMPATIBLE_%lu=", i);
+
+               entry = uevent_find_entry(buf, end, prefix, uevent);
+               if (entry == NULL) {
+                       while (i-- > 0)
+                               free(dev->id.compatible[i]);
+                       goto fail_id_compatible;
+               }
+
+               dev->id.compatible[i] = linedup(entry);
+               RTE_LOG(DEBUG, EAL, "%s(): %s%s\n", __func__, prefix,
+                               dev->id.compatible[i]);
+               if (dev->id.compatible[i] == NULL) {
+                       RTE_LOG(ERR, EAL, "%s(): failed to alloc memory\n",
+                               __func__);
+
+                       while (i-- > 0)
+                               free(dev->id.compatible[i]);
+                       goto fail_id_compatible;
+               }
+       }
+
+       dev->id.compatible[count] = NULL;
+       return 0;
+
+fail_id_compatible:
+       free(dev->id.compatible);
+fail_free_buf:
+       free(buf);
+fail_fclose:
+       fclose(f);
+       return -1;
+fail_free_buf_skip:
+       free(buf);
+fail_fclose_skip:
+       fclose(f);
+       return 1;
+}
+
+static void
+soc_device_uevent_free(struct rte_soc_device *dev)
+{
+       if (!dev)
+               return;
+
+       if (dev->id.compatible) {
+               int i;
+
+               for (i = 0; dev->id.compatible[i]; ++i)
+                       free(dev->id.compatible[i]);
+
+               free(dev->id.compatible);
+       }
+}
+
+static void
+soc_device_free(struct rte_soc_device *dev)
+{
+       soc_device_uevent_free(dev);
+       free(dev);
+}
+
+static int
+soc_scan_one(const char *dirname)
+{
+       char filename[PATH_MAX];
+       struct rte_soc_device *dev;
+       unsigned long tmp;
+       int rc;
+
+       dev = calloc(1, sizeof(*dev));
+       if (dev == NULL)
+               return -1;
+
+       snprintf(filename, sizeof(filename), "%s/numa_node", dirname);
+       if (access(filename, R_OK) != 0) {
+               /* no NUMA support */
+               dev->numa_node = 0;
+       } else {
+               if (eal_parse_sysfs_value(filename, &tmp) < 0) {
+                       free(dev);
+                       return -1;
+               }
+               dev->numa_node = tmp;
+       }
+
+       snprintf(filename, sizeof(filename), "%s/uevent", dirname);
+       rc = soc_device_from_uevent(dev, filename);
+       if (rc) {
+               free(dev);
+               return rc;
+       }
+
+       dev->driver = NULL;
+       dev->kdrv = RTE_SOC_KDRV_NONE;
+
+       if (TAILQ_EMPTY(&soc_device_list)) {
+               TAILQ_INSERT_TAIL(&soc_device_list, dev, next);
+       } else {
+               struct rte_soc_device *dev2;
+               int rc;
+
+               TAILQ_FOREACH(dev2, &soc_device_list, next) {
+                       rc = rte_eal_compare_soc_addr(&dev->addr, &dev2->addr);
+                       if (rc > 0)
+                               continue;
+
+                       if (rc < 0) {
+                               TAILQ_INSERT_BEFORE(dev2, dev, next);
+                       } else { /* already exists */
+                               dev2->kdrv = dev->kdrv;
+                               memmove(dev2->mem_resource, dev->mem_resource,
+                                               sizeof(dev->mem_resource));
+                               soc_device_free(dev);
+                       }
+                       return 0;
+               }
+               TAILQ_INSERT_TAIL(&soc_device_list, dev, next);
+       }
+
+       return 0;
+}
+
+int
+rte_eal_soc_scan(void)
+{
+       struct dirent *e;
+       DIR *dir;
+       char dirname[PATH_MAX];
+
+       dir = opendir(SYSFS_SOC_DEVICES);
+       if (dir == NULL) {
+               RTE_LOG(ERR, EAL, "%s(): opendir failed: %s\n",
+                       __func__, strerror(errno));
+               return -1;
+       }
+
+       while ((e = readdir(dir)) != NULL) {
+               if (e->d_name[0] == '.')
+                       continue;
+
+               snprintf(dirname, sizeof(dirname), "%s/%s", SYSFS_SOC_DEVICES,
+                               e->d_name);
+               if (soc_scan_one(dirname) < 0)
+                       goto error;
+       }
+
+       closedir(dir);
+       return 0;
+
+error:
+       closedir(dir);
+       return -1;
+}
+
+int
+rte_eal_soc_init(void)
+{
+       TAILQ_INIT(&soc_driver_list);
+       TAILQ_INIT(&soc_device_list);
+
+       if (internal_config.no_soc)
+               return 0;
+
+       if (rte_eal_soc_scan() < 0) {
+               RTE_LOG(ERR, EAL, "%s(): Failed to scan for SoC devices\n",
+                       __func__);
+               return -1;
+       }
+
+       return 0;
+}
-- 
2.6.3

Reply via email to