[dpdk-dev] [RFC 4/7] eal/linuxapp: support SoC infra in linuxapp

Jan Viktorin viktorin at rehivetech.com
Fri Jan 1 22:05:23 CET 2016


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



More information about the dev mailing list