From patchwork Wed Jan 25 10:38:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomasz Duszynski X-Patchwork-Id: 122514 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id CF86842483; Wed, 25 Jan 2023 11:38:22 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 701CF42D31; Wed, 25 Jan 2023 11:38:22 +0100 (CET) Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) by mails.dpdk.org (Postfix) with ESMTP id BFCA140684 for ; Wed, 25 Jan 2023 11:38:20 +0100 (CET) Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 30P7tgF0023368; Wed, 25 Jan 2023 02:38:19 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=pfpt0220; bh=nbq/oGpvCJMGRkR+1Icm51EWAR1ZUhaWXzsWPZ5MiPY=; b=Xpb8Kw4dKmN1ZUIy4fIxiZjquRFhBo88/ei+cB+fkASMeRpH271TIOb7UchCCufYIE4d YwRO+ZcYys4alGO8mO9VIwBNxc8B6ociiWtZ1009MmsMT1OjrkX8LpMn1Maq0XFHnLVG wVGzKYpzTuvVpKVHsg7zB74dYhC8a9UXh2meI7JHhPZwNM/nmQuO7bWDecH+q9CARKyY H7T+UQzre6Azs1MLersXoVlAjv78D7SWvsClxCYhHvEksFXWmNmX9OjJgv2X8toJz5xD HAEgXRzQynxqwPAdrCZpq66WlLrHmtxF1RzzDpkfkn6x6Y/ppLW+L1DsEAqurEvK7sNH wA== Received: from dc5-exch01.marvell.com ([199.233.59.181]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3nb0f68ncq-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Wed, 25 Jan 2023 02:38:19 -0800 Received: from DC5-EXCH02.marvell.com (10.69.176.39) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Wed, 25 Jan 2023 02:38:17 -0800 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH02.marvell.com (10.69.176.39) with Microsoft SMTP Server id 15.0.1497.42 via Frontend Transport; Wed, 25 Jan 2023 02:38:17 -0800 Received: from cavium-DT10.. (unknown [10.28.34.39]) by maili.marvell.com (Postfix) with ESMTP id 9F81A3F706F; Wed, 25 Jan 2023 02:38:14 -0800 (PST) From: Tomasz Duszynski To: CC: , , , , Tomasz Duszynski Subject: [PATCH 1/2] lib: add helper to read strings from sysfs files Date: Wed, 25 Jan 2023 11:38:08 +0100 Message-ID: <20230125103809.1250080-2-tduszynski@marvell.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230125103809.1250080-1-tduszynski@marvell.com> References: <20230125103809.1250080-1-tduszynski@marvell.com> MIME-Version: 1.0 X-Proofpoint-GUID: cyRrnTsYXwLlEvgMPmmFtod0Yvu0wJhF X-Proofpoint-ORIG-GUID: cyRrnTsYXwLlEvgMPmmFtod0Yvu0wJhF X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.219,Aquarius:18.0.930,Hydra:6.0.562,FMLib:17.11.122.1 definitions=2023-01-25_05,2023-01-25_01,2022-06-22_01 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Reading strings from sysfs files is a re-occurring pattern hence add helper for doing that. Signed-off-by: Tomasz Duszynski --- app/test/test_eal_fs.c | 108 ++++++++++++++++++++++++++++---- lib/eal/common/eal_filesystem.h | 6 ++ lib/eal/unix/eal_filesystem.c | 24 ++++--- lib/eal/version.map | 1 + 4 files changed, 121 insertions(+), 18 deletions(-) diff --git a/app/test/test_eal_fs.c b/app/test/test_eal_fs.c index b3686edcb4..6c373fc7f1 100644 --- a/app/test/test_eal_fs.c +++ b/app/test/test_eal_fs.c @@ -20,12 +20,33 @@ test_eal_fs(void) #else +static int +temp_create(char *filename, size_t len) +{ + char file_template[] = "/tmp/eal_test_XXXXXX"; + char proc_path[PATH_MAX]; + int fd; + + fd = mkstemp(file_template); + if (fd == -1) { + perror("mkstemp() failure"); + return -1; + } + + snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd); + if (readlink(proc_path, filename, len) < 0) { + perror("readlink() failure"); + close(fd); + return -1; + } + + return fd; +} + static int test_parse_sysfs_value(void) { char filename[PATH_MAX] = ""; - char proc_path[PATH_MAX]; - char file_template[] = "/tmp/eal_test_XXXXXX"; int tmp_file_handle = -1; FILE *fd = NULL; unsigned valid_number; @@ -40,16 +61,10 @@ test_parse_sysfs_value(void) /* get a temporary filename to use for all tests - create temp file handle and then * use /proc to get the actual file that we can open */ - tmp_file_handle = mkstemp(file_template); - if (tmp_file_handle == -1) { - perror("mkstemp() failure"); + tmp_file_handle = temp_create(filename, sizeof(filename)); + if (tmp_file_handle < 0) goto error; - } - snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", tmp_file_handle); - if (readlink(proc_path, filename, sizeof(filename)) < 0) { - perror("readlink() failure"); - goto error; - } + printf("Temporary file is: %s\n", filename); /* test we get an error value if we use file before it's created */ @@ -175,11 +190,82 @@ test_parse_sysfs_value(void) return -1; } +static int +test_parse_sysfs_string(void) +{ + const char *teststr = "the quick brown dog jumps over the lazy fox\n"; + char filename[PATH_MAX] = ""; + char buf[BUFSIZ] = { }; + int tmp_file_handle; + FILE *fd = NULL; + +#ifdef RTE_EXEC_ENV_FREEBSD + /* BSD doesn't have /proc/pid/fd */ + return 0; +#endif + printf("Testing function eal_parse_sysfs_string()\n"); + + /* get a temporary filename to use for all tests - create temp file handle and then + * use /proc to get the actual file that we can open + */ + tmp_file_handle = temp_create(filename, sizeof(filename)); + if (tmp_file_handle < 0) + goto error; + + printf("Temporary file is: %s\n", filename); + + /* test we get an error value if we use file before it's created */ + printf("Test reading a missing file ...\n"); + if (eal_parse_sysfs_string("/dev/not-quite-null", buf, sizeof(buf)) == 0) { + printf("Error with eal_parse_sysfs_string() - returned success on reading empty file\n"); + goto error; + } + printf("Confirmed return error when reading empty file\n"); + + /* test reading a string from file */ + printf("Test reading string ...\n"); + fd = fopen(filename, "w"); + if (fd == NULL) { + printf("line %d, Error opening %s: %s\n", __LINE__, filename, strerror(errno)); + goto error; + } + fprintf(fd, "%s", teststr); + fclose(fd); + fd = NULL; + if (eal_parse_sysfs_string(filename, buf, sizeof(buf) - 1) < 0) { + printf("eal_parse_sysfs_string() returned error - test failed\n"); + goto error; + } + if (strcmp(teststr, buf)) { + printf("Invalid string read by eal_parse_sysfs_string() - test failed\n"); + goto error; + } + /* don't print newline */ + buf[strlen(buf) - 1] = '\0'; + printf("Read '%s\\n' ok\n", buf); + + close(tmp_file_handle); + unlink(filename); + printf("eal_parse_sysfs_string() - OK\n"); + return 0; + +error: + if (fd) + fclose(fd); + if (tmp_file_handle > 0) + close(tmp_file_handle); + if (filename[0] != '\0') + unlink(filename); + return -1; +} + static int test_eal_fs(void) { if (test_parse_sysfs_value() < 0) return -1; + if (test_parse_sysfs_string() < 0) + return -1; return 0; } diff --git a/lib/eal/common/eal_filesystem.h b/lib/eal/common/eal_filesystem.h index 5d21f07c20..ac6449f529 100644 --- a/lib/eal/common/eal_filesystem.h +++ b/lib/eal/common/eal_filesystem.h @@ -104,4 +104,10 @@ eal_get_hugefile_path(char *buffer, size_t buflen, const char *hugedir, int f_id * Used to read information from files on /sys */ int eal_parse_sysfs_value(const char *filename, unsigned long *val); +/** Function to read a string from a file on the filesystem. + * Used to read information for files in /sys + */ +__rte_internal +int eal_parse_sysfs_string(const char *filename, char *str, size_t size); + #endif /* EAL_FILESYSTEM_H */ diff --git a/lib/eal/unix/eal_filesystem.c b/lib/eal/unix/eal_filesystem.c index afbab9368a..8ed10094be 100644 --- a/lib/eal/unix/eal_filesystem.c +++ b/lib/eal/unix/eal_filesystem.c @@ -76,12 +76,9 @@ int eal_create_runtime_dir(void) return 0; } -/* parse a sysfs (or other) file containing one integer value */ -int eal_parse_sysfs_value(const char *filename, unsigned long *val) +int eal_parse_sysfs_string(const char *filename, char *str, size_t size) { FILE *f; - char buf[BUFSIZ]; - char *end = NULL; if ((f = fopen(filename, "r")) == NULL) { RTE_LOG(ERR, EAL, "%s(): cannot open sysfs value %s\n", @@ -89,19 +86,32 @@ int eal_parse_sysfs_value(const char *filename, unsigned long *val) return -1; } - if (fgets(buf, sizeof(buf), f) == NULL) { + if (fgets(str, size, f) == NULL) { RTE_LOG(ERR, EAL, "%s(): cannot read sysfs value %s\n", __func__, filename); fclose(f); return -1; } + fclose(f); + return 0; +} + +/* parse a sysfs (or other) file containing one integer value */ +int eal_parse_sysfs_value(const char *filename, unsigned long *val) +{ + char buf[BUFSIZ]; + char *end = NULL; + int ret; + + ret = eal_parse_sysfs_string(filename, buf, sizeof(buf)); + if (ret < 0) + return ret; + *val = strtoul(buf, &end, 0); if ((buf[0] == '\0') || (end == NULL) || (*end != '\n')) { RTE_LOG(ERR, EAL, "%s(): cannot parse sysfs value %s\n", __func__, filename); - fclose(f); return -1; } - fclose(f); return 0; } diff --git a/lib/eal/version.map b/lib/eal/version.map index 7ad12a7dc9..9118bb6228 100644 --- a/lib/eal/version.map +++ b/lib/eal/version.map @@ -445,6 +445,7 @@ EXPERIMENTAL { INTERNAL { global: + eal_parse_sysfs_string; # WINDOWS_NO_EXPORT rte_bus_register; rte_bus_unregister; rte_eal_get_baseaddr; From patchwork Wed Jan 25 10:38:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomasz Duszynski X-Patchwork-Id: 122516 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 8843A42483; Wed, 25 Jan 2023 11:38:32 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 2108542D74; Wed, 25 Jan 2023 11:38:25 +0100 (CET) Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) by mails.dpdk.org (Postfix) with ESMTP id 6385F42D66 for ; Wed, 25 Jan 2023 11:38:23 +0100 (CET) Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 30P7tYiT023280; Wed, 25 Jan 2023 02:38:22 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=pfpt0220; bh=qmUt/mcDzUOWgLapG/BYBGra8aw3MJq86fGqD8cvXCw=; b=lO56bUNtSKoLbet+UaG0ZFSHDG7BNVkhTaKokq8l64/HpbDHayLI5le/vgNygAPZeuOC uYBm53GhSBfSV0X75j84XAxsleUl/S+uLhNHkGS8YDV70m89dg0ai4VkI42GnUyatjLF tRWu8JBos2HwFhCJ/ODpvv08x9wNtWw3KCcH/1xjLs1lvi3NN3JpsFV1gLTZ/IoD5vLt jh7xpV5du2wnF5LVgZ+6tPPlR8zWcTe5ypTE/tfu58bjZJCij0ZRl8oxQ7ltsSnkNJBR 6HF1lIduVt1u9TwLSVmDVTDsm8HhQWE/+gxaTZ/kSSjNeLA2KLEEM5DcLjrZtgnjJrsN YA== Received: from dc5-exch02.marvell.com ([199.233.59.182]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3nb0f68ncr-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Wed, 25 Jan 2023 02:38:22 -0800 Received: from DC5-EXCH02.marvell.com (10.69.176.39) by DC5-EXCH02.marvell.com (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Wed, 25 Jan 2023 02:38:19 -0800 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH02.marvell.com (10.69.176.39) with Microsoft SMTP Server id 15.0.1497.42 via Frontend Transport; Wed, 25 Jan 2023 02:38:19 -0800 Received: from cavium-DT10.. (unknown [10.28.34.39]) by maili.marvell.com (Postfix) with ESMTP id 34FFC3F706A; Wed, 25 Jan 2023 02:38:16 -0800 (PST) From: Tomasz Duszynski To: , Thomas Monjalon , Tomasz Duszynski CC: , , Subject: [PATCH 2/2] bus: add platform bus Date: Wed, 25 Jan 2023 11:38:09 +0100 Message-ID: <20230125103809.1250080-3-tduszynski@marvell.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230125103809.1250080-1-tduszynski@marvell.com> References: <20230125103809.1250080-1-tduszynski@marvell.com> MIME-Version: 1.0 X-Proofpoint-GUID: hRYyJb2YFet1mCMa-ydBKnerbpy6c8Jc X-Proofpoint-ORIG-GUID: hRYyJb2YFet1mCMa-ydBKnerbpy6c8Jc X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.219,Aquarius:18.0.930,Hydra:6.0.562,FMLib:17.11.122.1 definitions=2023-01-25_05,2023-01-25_01,2022-06-22_01 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Platform bus is a software bus under Linux that manages devices which generally do not have built-in discovery mechanisms. Linux normally learns about platform devices directly from device-tree during boot-up phase. Up to this point, whenever some userspace app needed control over platform device or a range of thereof some sort of driver being a mixture of vdev/rawdev was required. In order to simplify this task, provide an auto-probe experience and separate bus logic from the driver itself, add platform bus support. Currently devices backed up by vfio-platform kernel driver are supported. Signed-off-by: Tomasz Duszynski --- MAINTAINERS | 4 + doc/guides/rel_notes/release_23_03.rst | 5 + drivers/bus/meson.build | 1 + drivers/bus/platform/bus_platform_driver.h | 174 ++++++ drivers/bus/platform/meson.build | 16 + drivers/bus/platform/platform.c | 604 +++++++++++++++++++++ drivers/bus/platform/platform_params.c | 70 +++ drivers/bus/platform/private.h | 48 ++ drivers/bus/platform/version.map | 10 + 9 files changed, 932 insertions(+) create mode 100644 drivers/bus/platform/bus_platform_driver.h create mode 100644 drivers/bus/platform/meson.build create mode 100644 drivers/bus/platform/platform.c create mode 100644 drivers/bus/platform/platform_params.c create mode 100644 drivers/bus/platform/private.h create mode 100644 drivers/bus/platform/version.map diff --git a/MAINTAINERS b/MAINTAINERS index 9a0f416d2e..b02666710c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -581,6 +581,10 @@ VDEV bus driver F: drivers/bus/vdev/ F: app/test/test_vdev.c +Platform bus driver +M: Tomasz Duszynski +F: drivers/bus/platform + VMBUS bus driver M: Long Li F: drivers/bus/vmbus/ diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst index 84b112a8b1..74b2b1e3ff 100644 --- a/doc/guides/rel_notes/release_23_03.rst +++ b/doc/guides/rel_notes/release_23_03.rst @@ -57,6 +57,11 @@ New Features * **Added multi-process support for axgbe PMD.** +* **Added platform bus support.** + + A platform bus provides a way to use Linux platform devices which + are compatible with vfio-platform kernel driver. + * **Updated Corigine nfp driver.** * Added support for meter options. diff --git a/drivers/bus/meson.build b/drivers/bus/meson.build index 45eab5233d..6d2520c543 100644 --- a/drivers/bus/meson.build +++ b/drivers/bus/meson.build @@ -7,6 +7,7 @@ drivers = [ 'fslmc', 'ifpga', 'pci', + 'platform', 'vdev', 'vmbus', ] diff --git a/drivers/bus/platform/bus_platform_driver.h b/drivers/bus/platform/bus_platform_driver.h new file mode 100644 index 0000000000..8291c7f3f6 --- /dev/null +++ b/drivers/bus/platform/bus_platform_driver.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef _BUS_PLATFORM_DRIVER_H_ +#define _BUS_PLATFORM_DRIVER_H_ + +/** + * @file + * Platform bus interface. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include +#include +#include + +/* Forward declarations */ +struct rte_platform_bus; +struct rte_platform_device; +struct rte_platform_driver; + +/** + * Initialization function for the driver called during platform device probing. + * + * @param pdev + * Pointer to the platform device. + * @return + * 0 on success, negative value otherwise. + */ +typedef int (rte_platform_probe_t)(struct rte_platform_device *pdev); + +/** + * Removal function for the driver called during platform device removal. + * + * @param pdev + * Pointer to the platform device. + * @return + * 0 on success, negative value otherwise. + */ +typedef int (rte_platform_remove_t)(struct rte_platform_device *pdev); + +/** + * Driver specific DMA mapping. + * + * @param pdev + * Pointer to the platform device. + * @param addr + * Starting virtual address of memory to be mapped. + * @param iova + * Starting IOVA address of memory to be mapped. + * @param len + * Length of memory segment being mapped. + * @return + * - 0 on success, negative value and rte_errno is set otherwise. + */ +typedef int (rte_platform_dma_map_t)(struct rte_platform_device *pdev, void *addr, uint64_t iova, + size_t len); + +/** + * Driver specific DMA unmapping. + * + * @param pdev + * Pointer to the platform device. + * @param addr + * Starting virtual address of memory to be mapped. + * @param iova + * Starting IOVA address of memory to be mapped. + * @param len + * Length of memory segment being mapped. + * @return + * - 0 on success, negative value and rte_errno is set otherwise. + */ +typedef int (rte_platform_dma_unmap_t)(struct rte_platform_device *pdev, void *addr, uint64_t iova, + size_t len); + +/** + * A structure describing a platform device resource. + */ +struct rte_platform_resource { + char *name; /**< Resource name specified via reg-names prop in device-tree */ + struct rte_mem_resource mem; /**< Memory resource */ +}; + +/** + * A structure describing a platform device. + */ +struct rte_platform_device { + RTE_TAILQ_ENTRY(rte_platform_device) next; /**< Next attached platform device */ + struct rte_device device; /**< Core device */ + struct rte_platform_driver *driver; /**< Matching device driver */ + char name[RTE_DEV_NAME_MAX_LEN]; /**< Device name */ + unsigned int num_resource; /**< Number of device resources */ + struct rte_platform_resource *resource; /**< Device resources */ + int dev_fd; /**< VFIO device fd */ +}; + +/** + * A structure describing a platform device driver. + */ +struct rte_platform_driver { + RTE_TAILQ_ENTRY(rte_platform_driver) next; /**< Next available platform driver */ + struct rte_driver driver; /**< Core driver */ + rte_platform_probe_t *probe; /**< Device probe function */ + rte_platform_remove_t *remove; /**< Device remove function */ + rte_platform_dma_map_t *dma_map; /**< Device DMA map function */ + rte_platform_dma_unmap_t *dma_unmap; /**< Device DMA unmap function */ + uint32_t drv_flags; /**< Driver flags RTE_PLATFORM_DRV_* */ +}; + +/** Device driver needs IOVA as VA and cannot work with IOVA as PA */ +#define RTE_PLATFORM_DRV_NEED_IOVA_AS_VA 0x0001 + +/** + * @internal + * Helper macros used to convert core device to platform device. + */ +#define RTE_DEV_TO_PLATFORM_DEV(ptr) \ + container_of(ptr, struct rte_platform_device, device) + +#define RTE_DEV_TO_PLATFORM_DEV_CONST(ptr) \ + container_of(ptr, const struct rte_platform_device, device) + +/** + * Register a platform device driver. + * + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * @param pdrv + * A pointer to a rte_platform_driver structure describing driver to be registered. + */ +__rte_internal +void rte_platform_register(struct rte_platform_driver *pdrv); + +/** Helper for platform driver registration. */ +#define RTE_PMD_REGISTER_PLATFORM(nm, platform_drv) \ +static const char *pdrvinit_ ## nm ## _alias; \ +RTE_INIT(pdrvinitfn_ ##nm) \ +{ \ + (platform_drv).driver.name = RTE_STR(nm); \ + (platform_drv).driver.alias = pdrvinit_ ## nm ## _alias; \ + rte_platform_register(&(platform_drv)); \ +} \ +RTE_PMD_EXPORT_NAME(nm, __COUNTER__) + +/** Helper for setting platform driver alias. */ +#define RTE_PMD_REGISTER_ALIAS(nm, alias) \ +static const char *pdrvinit_ ## nm ## _alias = RTE_STR(alias) + +/** + * Unregister a platform device driver. + * + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * @param pdrv + * A pointer to a rte_platform_driver structure describing driver to be unregistered. + */ +__rte_internal +void rte_platform_unregister(struct rte_platform_driver *pdrv); + +#ifdef __cplusplus +} +#endif + +#endif /* _BUS_PLATFORM_DRIVER_H_ */ diff --git a/drivers/bus/platform/meson.build b/drivers/bus/platform/meson.build new file mode 100644 index 0000000000..417d7b81f8 --- /dev/null +++ b/drivers/bus/platform/meson.build @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2023 Marvell. +# + +if not is_linux + build = false + reason = 'only supported on Linux' + subdir_done() +endif + +deps += ['kvargs'] +sources = files( + 'platform_params.c', + 'platform.c', +) +driver_sdk_headers += files('bus_platform_driver.h') diff --git a/drivers/bus/platform/platform.c b/drivers/bus/platform/platform.c new file mode 100644 index 0000000000..b43a5b9153 --- /dev/null +++ b/drivers/bus/platform/platform.c @@ -0,0 +1,604 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "private.h" + +#define PLATFORM_BUS_DEVICES_PATH "/sys/bus/platform/devices" + +void +rte_platform_register(struct rte_platform_driver *pdrv) +{ + TAILQ_INSERT_TAIL(&platform_bus.driver_list, pdrv, next); +} + +void +rte_platform_unregister(struct rte_platform_driver *pdrv) +{ + TAILQ_REMOVE(&platform_bus.driver_list, pdrv, next); +} + +static struct rte_devargs * +dev_devargs(const char *dev_name) +{ + struct rte_devargs *devargs; + + RTE_EAL_DEVARGS_FOREACH("platform", devargs) { + if (!strcmp(devargs->name, dev_name)) + return devargs; + } + + return NULL; +} + +static bool +dev_allowed(const char *dev_name) +{ + struct rte_devargs *devargs; + + devargs = dev_devargs(dev_name); + if (devargs == NULL) + return true; + + switch (platform_bus.bus.conf.scan_mode) { + case RTE_BUS_SCAN_UNDEFINED: + case RTE_BUS_SCAN_ALLOWLIST: + if (devargs->policy == RTE_DEV_ALLOWED) + return true; + break; + case RTE_BUS_SCAN_BLOCKLIST: + if (devargs->policy == RTE_DEV_BLOCKED) + return false; + break; + } + + return true; +} + +static int +dev_add(const char *dev_name) +{ + struct rte_platform_device *pdev, *tmp; + char path[PATH_MAX]; + unsigned long val; + + pdev = calloc(1, sizeof(*pdev)); + if (pdev == NULL) + return -ENOMEM; + + rte_strscpy(pdev->name, dev_name, sizeof(pdev->name)); + pdev->device.name = pdev->name; + pdev->device.devargs = dev_devargs(dev_name); + pdev->device.bus = &platform_bus.bus; + snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/numa_node", dev_name); + pdev->device.numa_node = eal_parse_sysfs_value(path, &val) ? rte_socket_id() : val; + + FOREACH_DEVICE_ON_PLATFORM_BUS(tmp) { + if (!strcmp(tmp->name, pdev->name)) { + PLATFORM_LOG(INFO, "device %s already added\n", pdev->name); + + if (tmp->device.devargs != pdev->device.devargs) + rte_devargs_remove(pdev->device.devargs); + + free(pdev); + } + } + + TAILQ_INSERT_HEAD(&platform_bus.device_list, pdev, next); + + PLATFORM_LOG(INFO, "adding device %s to the list\n", dev_name); + + return 0; +} + +static char * +dev_kernel_driver_name(const char *dev_name) +{ + char path[PATH_MAX], buf[BUFSIZ] = { }; + char *kdrv; + int ret; + + snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/driver", dev_name); + /* save space for NUL */ + ret = readlink(path, buf, sizeof(buf) - 1); + if (ret <= 0) + return NULL; + + /* last token is kernel driver name */ + kdrv = strrchr(buf, '/'); + if (kdrv != NULL) + return strdup(kdrv + 1); + + return NULL; +} + +static bool +dev_is_bound_vfio_platform(const char *dev_name) +{ + char *kdrv; + int ret; + + kdrv = dev_kernel_driver_name(dev_name); + if (!kdrv) + return false; + + ret = strcmp(kdrv, "vfio-platform"); + free(kdrv); + + return ret == 0; +} + +static int +platform_bus_scan(void) +{ + const struct dirent *ent; + const char *dev_name; + int ret = 0; + DIR *dp; + + if ((dp = opendir(PLATFORM_BUS_DEVICES_PATH)) == NULL) { + PLATFORM_LOG(INFO, "failed to open %s\n", PLATFORM_BUS_DEVICES_PATH); + return -errno; + } + + while ((ent = readdir(dp))) { + dev_name = ent->d_name; + if (dev_name[0] == '.') + continue; + + if (!dev_allowed(dev_name)) + continue; + + if (!dev_is_bound_vfio_platform(dev_name)) + continue; + + ret = dev_add(dev_name); + if (ret) + break; + } + + closedir(dp); + + return ret; +} + +static int +device_map_resource_offset(struct rte_platform_device *pdev, struct rte_platform_resource *res, + size_t offset) +{ + res->mem.addr = mmap(NULL, res->mem.len, PROT_READ | PROT_WRITE, MAP_PRIVATE, pdev->dev_fd, + offset); + if (res->mem.addr == MAP_FAILED) + return -errno; + + PLATFORM_LOG(DEBUG, "adding resource va = %p len = %"PRIu64" name = %s\n", res->mem.addr, + res->mem.len, res->name); + + return 0; +} + +static void +device_unmap_resources(struct rte_platform_device *pdev) +{ + struct rte_platform_resource *res; + unsigned int i; + + for (i = 0; i < pdev->num_resource; i++) { + res = &pdev->resource[i]; + munmap(res->mem.addr, res->mem.len); + free(res->name); + } + + free(pdev->resource); + pdev->resource = NULL; + pdev->num_resource = 0; +} + +static char * +of_resource_name(const char *dev_name, int index) +{ + char path[PATH_MAX], buf[BUFSIZ] = { }; + int num = 0, ret; + char *name; + + snprintf(path, sizeof(path), PLATFORM_BUS_DEVICES_PATH "/%s/of_node/reg-names", dev_name); + ret = eal_parse_sysfs_string(path, buf, sizeof(buf) - 1); + if (ret) + return NULL; + + for (name = buf; name; name += strlen(name) + 1) { + if (num++ != index) + continue; + return strdup(name); + } + + return NULL; +} + +static int +device_map_resources(struct rte_platform_device *pdev, unsigned int num) +{ + struct rte_platform_resource *res; + unsigned int i; + int ret; + + if (num == 0) + PLATFORM_LOG(WARNING, "device %s has no resources\n", pdev->name); + + pdev->resource = calloc(num, sizeof(*pdev->resource)); + if (pdev->resource == NULL) + return -ENOMEM; + + for (i = 0; i < num; i++) { + struct vfio_region_info reg_info = { + .argsz = sizeof(reg_info), + .index = i, + }; + + ret = ioctl(pdev->dev_fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + if (ret) { + PLATFORM_LOG(ERR, "failed to get region info at %d\n", i); + ret = -errno; + goto out; + } + + res = &pdev->resource[i]; + res->name = of_resource_name(pdev->name, reg_info.index); + res->mem.len = reg_info.size; + ret = device_map_resource_offset(pdev, res, reg_info.offset); + if (ret) { + PLATFORM_LOG(ERR, "failed to ioremap resource at %d\n", i); + goto out; + } + + pdev->num_resource++; + } + + return 0; +out: + device_unmap_resources(pdev); + + return ret; +} + +static void +device_cleanup(struct rte_platform_device *pdev) +{ + device_unmap_resources(pdev); + rte_vfio_release_device(PLATFORM_BUS_DEVICES_PATH, pdev->name, pdev->dev_fd); +} + +static int +device_setup(struct rte_platform_device *pdev) +{ + struct vfio_device_info dev_info = { .argsz = sizeof(dev_info), }; + const char *name = pdev->name; + int ret; + + ret = rte_vfio_setup_device(PLATFORM_BUS_DEVICES_PATH, name, &pdev->dev_fd, &dev_info); + if (ret) { + PLATFORM_LOG(ERR, "failed to setup %s\n", name); + return -ENODEV; + } + + if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PLATFORM)) { + PLATFORM_LOG(ERR, "device not backed by vfio-platform\n"); + ret = -ENOTSUP; + goto out; + } + + ret = device_map_resources(pdev, dev_info.num_regions); + if (ret) { + PLATFORM_LOG(ERR, "failed to setup platform resources\n"); + goto out; + } + + return 0; +out: + device_cleanup(pdev); + + return ret; +} + +static int +driver_call_probe(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev) +{ + int ret; + + if (rte_dev_is_probed(&pdev->device)) + return -EBUSY; + + if (pdrv->probe) { + pdev->driver = pdrv; + ret = pdrv->probe(pdev); + if (ret) + return ret; + } + + pdev->device.driver = &pdrv->driver; + + return 0; +} + +static int +driver_probe_device(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev) +{ + enum rte_iova_mode iova_mode; + int ret; + + iova_mode = rte_eal_iova_mode(); + if (pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA && iova_mode != RTE_IOVA_VA) { + PLATFORM_LOG(ERR, "driver %s expects VA IOVA mode but current mode is PA\n", + pdrv->driver.name); + return -EINVAL; + } + + ret = device_setup(pdev); + if (ret) + return ret; + + ret = driver_call_probe(pdrv, pdev); + if (ret) + device_cleanup(pdev); + + return ret; +} + +static bool +driver_match_device(struct rte_platform_driver *pdrv, struct rte_platform_device *pdev) +{ + bool match = false; + char *kdrv; + + kdrv = dev_kernel_driver_name(pdev->name); + if (!kdrv) + return false; + + /* match by driver name */ + if (!strcmp(kdrv, pdrv->driver.name)) { + match = true; + goto out; + } + + /* match by driver alias */ + if (pdrv->driver.alias != NULL && !strcmp(kdrv, pdrv->driver.alias)) { + match = true; + goto out; + } + + /* match by device name */ + if (!strcmp(pdev->name, pdrv->driver.name)) + match = true; + +out: + free(kdrv); + + return match; +} + + +static int +device_attach(struct rte_platform_device *pdev) +{ + struct rte_platform_driver *pdrv; + + FOREACH_DRIVER_ON_PLATFORM_BUS(pdrv) { + if (driver_match_device(pdrv, pdev)) + break; + } + + if (pdrv == NULL) + return -ENODEV; + + return driver_probe_device(pdrv, pdev); +} + +static int +platform_bus_probe(void) +{ + struct rte_platform_device *pdev; + int ret; + + FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) { + ret = device_attach(pdev); + if (ret == -EBUSY) { + PLATFORM_LOG(DEBUG, "device %s already probed\n", pdev->name); + continue; + } + if (ret) + PLATFORM_LOG(ERR, "failed to probe %s\n", pdev->name); + } + + return 0; +} + +static struct rte_device * +platform_bus_find_device(const struct rte_device *start, rte_dev_cmp_t cmp, const void *data) +{ + struct rte_platform_device *pdev; + + pdev = start ? RTE_TAILQ_NEXT(RTE_DEV_TO_PLATFORM_DEV_CONST(start), next) : + RTE_TAILQ_FIRST(&platform_bus.device_list); + while (pdev) { + if (cmp(&pdev->device, data) == 0) + return &pdev->device; + + pdev = RTE_TAILQ_NEXT(pdev, next); + } + + return NULL; +} + +static int +platform_bus_plug(struct rte_device *dev) +{ + struct rte_platform_device *pdev; + + if (!dev_allowed(dev->name)) + return -EPERM; + + if (!dev_is_bound_vfio_platform(dev->name)) + return -EPERM; + + pdev = RTE_DEV_TO_PLATFORM_DEV(dev); + if (pdev == NULL) + return -EINVAL; + + return device_attach(pdev); +} + +static void +device_release_driver(struct rte_platform_device *pdev) +{ + struct rte_platform_driver *pdrv; + int ret; + + pdrv = pdev->driver; + if (pdrv != NULL && pdrv->remove != NULL) { + ret = pdrv->remove(pdev); + if (ret) + PLATFORM_LOG(WARNING, "failed to remove %s\n", pdev->name); + } + + pdev->device.driver = NULL; + pdev->driver = NULL; +} + +static int +platform_bus_unplug(struct rte_device *dev) +{ + struct rte_platform_device *pdev; + + pdev = RTE_DEV_TO_PLATFORM_DEV(dev); + if (pdev == NULL) + return -EINVAL; + + device_release_driver(pdev); + device_cleanup(pdev); + rte_devargs_remove(pdev->device.devargs); + free(pdev); + + return 0; +} + +static int +platform_bus_parse(const char *name, void *addr) +{ + struct rte_platform_device *pdev; + const char **out = addr; + + FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) { + if (!strcmp(name, pdev->name)) + break; + } + + if (pdev && addr) + *out = name; + + return pdev ? 0 : -ENODEV; +} + +static int +platform_bus_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len) +{ + struct rte_platform_device *pdev; + + pdev = RTE_DEV_TO_PLATFORM_DEV(dev); + if (pdev == NULL || pdev->driver == NULL) { + rte_errno = EINVAL; + return -1; + } + + if (pdev->driver->dma_map != NULL) + return pdev->driver->dma_map(pdev, addr, iova, len); + + return rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova, len); +} + +static int +platform_bus_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len) +{ + struct rte_platform_device *pdev; + + pdev = RTE_DEV_TO_PLATFORM_DEV(dev); + if (pdev == NULL || pdev->driver == NULL) { + rte_errno = EINVAL; + return -1; + } + + if (pdev->driver->dma_unmap != NULL) + return pdev->driver->dma_unmap(pdev, addr, iova, len); + + return rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD, (uint64_t)addr, iova, + len); +} + +static enum rte_iova_mode +platform_bus_get_iommu_class(void) +{ + struct rte_platform_driver *pdrv; + struct rte_platform_device *pdev; + + FOREACH_DEVICE_ON_PLATFORM_BUS(pdev) { + pdrv = pdev->driver; + if (pdrv != NULL && pdrv->drv_flags & RTE_PLATFORM_DRV_NEED_IOVA_AS_VA) + return RTE_IOVA_VA; + } + + return RTE_IOVA_DC; +} + +static int +platform_bus_cleanup(void) +{ + struct rte_platform_device *pdev, *tmp; + + RTE_TAILQ_FOREACH_SAFE(pdev, &platform_bus.device_list, next, tmp) { + platform_bus_unplug(&pdev->device); + TAILQ_REMOVE(&platform_bus.device_list, pdev, next); + } + + return 0; +} + +struct rte_platform_bus platform_bus = { + .bus = { + .scan = platform_bus_scan, + .probe = platform_bus_probe, + .find_device = platform_bus_find_device, + .plug = platform_bus_plug, + .unplug = platform_bus_unplug, + .parse = platform_bus_parse, + .dma_map = platform_bus_dma_map, + .dma_unmap = platform_bus_dma_unmap, + .get_iommu_class = platform_bus_get_iommu_class, + .dev_iterate = platform_bus_dev_iterate, + .cleanup = platform_bus_cleanup, + }, + .device_list = TAILQ_HEAD_INITIALIZER(platform_bus.device_list), + .driver_list = TAILQ_HEAD_INITIALIZER(platform_bus.driver_list), +}; + +RTE_REGISTER_BUS(platform_bus, platform_bus.bus); +RTE_LOG_REGISTER_DEFAULT(platform_bus_logtype, NOTICE); diff --git a/drivers/bus/platform/platform_params.c b/drivers/bus/platform/platform_params.c new file mode 100644 index 0000000000..d199c0c586 --- /dev/null +++ b/drivers/bus/platform/platform_params.c @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "bus_platform_driver.h" +#include "private.h" + +enum platform_params { + RTE_PLATFORM_PARAM_NAME, +}; + +static const char * const platform_params_keys[] = { + [RTE_PLATFORM_PARAM_NAME] = "name", + NULL +}; + +static int +platform_dev_match(const struct rte_device *dev, const void *_kvlist) +{ + const char *key = platform_params_keys[RTE_PLATFORM_PARAM_NAME]; + const struct rte_kvargs *kvlist = _kvlist; + const char *name; + + /* no kvlist arg, all devices match */ + if (kvlist == NULL) + return 0; + + /* if key is present in kvlist and does not match, filter device */ + name = rte_kvargs_get(kvlist, key); + if (name != NULL && strcmp(name, dev->name)) + return -1; + + return 0; +} + +void * +platform_bus_dev_iterate(const void *start, const char *str, + const struct rte_dev_iterator *it __rte_unused) +{ + rte_bus_find_device_t find_device; + struct rte_kvargs *kvargs = NULL; + struct rte_device *dev; + + if (str != NULL) { + kvargs = rte_kvargs_parse(str, platform_params_keys); + if (!kvargs) { + PLATFORM_LOG(ERR, "cannot parse argument list %s", str); + rte_errno = EINVAL; + return NULL; + } + } + + find_device = platform_bus.bus.find_device; + if (find_device == NULL) + return NULL; + + dev = platform_bus.bus.find_device(start, platform_dev_match, kvargs); + rte_kvargs_free(kvargs); + + return dev; +} diff --git a/drivers/bus/platform/private.h b/drivers/bus/platform/private.h new file mode 100644 index 0000000000..dcd992f8a7 --- /dev/null +++ b/drivers/bus/platform/private.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifndef _PLATFORM_PRIVATE_H_ +#define _PLATFORM_PRIVATE_H_ + +#include +#include +#include +#include +#include +#include + +#include "bus_platform_driver.h" + +extern struct rte_platform_bus platform_bus; +extern int platform_bus_logtype; + +/* Platform bus iterators. */ +#define FOREACH_DEVICE_ON_PLATFORM_BUS(p) \ + RTE_TAILQ_FOREACH(p, &(platform_bus.device_list), next) + +#define FOREACH_DRIVER_ON_PLATFORM_BUS(p) \ + RTE_TAILQ_FOREACH(p, &(platform_bus.driver_list), next) + +/* + * Structure describing platform bus. + */ +struct rte_platform_bus { + struct rte_bus bus; /* Core bus */ + RTE_TAILQ_HEAD(, rte_platform_device) device_list; /* List of bus devices */ + RTE_TAILQ_HEAD(, rte_platform_driver) driver_list; /* List of bus drivers */ +}; + +#define PLATFORM_LOG(level, ...) \ + rte_log(RTE_LOG_ ## level, platform_bus_logtype, \ + RTE_FMT("platform bus: " RTE_FMT_HEAD(__VA_ARGS__,), \ + RTE_FMT_TAIL(__VA_ARGS__,))) + +/* + * Iterate registered platform devices and find one that matches provided string. + */ +void * +platform_bus_dev_iterate(const void *start, const char *str, + const struct rte_dev_iterator *it __rte_unused); + +#endif /* _PLATFORM_PRIVATE_H_ */ diff --git a/drivers/bus/platform/version.map b/drivers/bus/platform/version.map new file mode 100644 index 0000000000..bacce4da08 --- /dev/null +++ b/drivers/bus/platform/version.map @@ -0,0 +1,10 @@ +DPDK_23 { + local: *; +}; + +INTERNAL { + global: + + rte_platform_register; + rte_platform_unregister; +};