From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from wes1-so2.wedos.net (wes1-so2.wedos.net [46.28.106.16]) by dpdk.org (Postfix) with ESMTP id DBC3F5A26 for ; Fri, 1 Jan 2016 22:06:52 +0100 (CET) Received: from pcviktorin.fit.vutbr.cz (pcviktorin.fit.vutbr.cz [147.229.13.147]) by wes1-so2.wedos.net (Postfix) with ESMTPSA id 3pXJnS3ZzKzrJ; Fri, 1 Jan 2016 22:06:52 +0100 (CET) From: Jan Viktorin To: dev@dpdk.org Date: Fri, 1 Jan 2016 22:05:23 +0100 Message-Id: <1451682326-5834-5-git-send-email-viktorin@rehivetech.com> X-Mailer: git-send-email 2.6.3 In-Reply-To: <1451682326-5834-1-git-send-email-viktorin@rehivetech.com> References: <1451682326-5834-1-git-send-email-viktorin@rehivetech.com> Cc: Jan Viktorin Subject: [dpdk-dev] [RFC 4/7] eal/linuxapp: support SoC infra in linuxapp X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 01 Jan 2016 21:06:53 -0000 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 --- 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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