From patchwork Sun Feb 5 18:38:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomasz Duszynski X-Patchwork-Id: 123063 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 1632E41BDD; Sun, 5 Feb 2023 19:39:09 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id E010440A7D; Sun, 5 Feb 2023 19:39:08 +0100 (CET) Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) by mails.dpdk.org (Postfix) with ESMTP id 0AF7D40041 for ; Sun, 5 Feb 2023 19:39:06 +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 315Dim9U013612; Sun, 5 Feb 2023 10:39:06 -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=JP+CKRcZLYa59cRqrO5PJKYBW7Jz3DiuIC3/i8AnTeM=; b=jKMSXFj15dvn3XBYYNyq/yKPJ4OWi3TVtxqJzK0tN1WVMn1fq+ll+PutedbQ348dEAlo hGae48JNBSyfpynb1pYWpTbACHnS0XsTydlD2MBQQ6ex6TewVCsWvtk6dhZkVWFyEVVB nM3SiyHYr/Ki8Y/lX1FCR7VOqy4QExaNWHK76XCRtWZrP17BPaosOJE54jVGs2tCpuEA jkMgInb+C0wFQTDldlmS8F6hFgzjioX/PIQdR0vq2ayneE03S1zStNm8/obNLcb6xkcQ 4Zs+d53hN9mppP5TXzIm+L6VeoD7Iio1ZAevzdo1SxcMzLCfjEMd4wdbMm+kCYHKOxPG ig== Received: from dc5-exch02.marvell.com ([199.233.59.182]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3nhqrt5r8c-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Sun, 05 Feb 2023 10:39:05 -0800 Received: from DC5-EXCH01.marvell.com (10.69.176.38) by DC5-EXCH02.marvell.com (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.42; Sun, 5 Feb 2023 10:39:03 -0800 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server id 15.0.1497.42 via Frontend Transport; Sun, 5 Feb 2023 10:39:03 -0800 Received: from cavium-DT10.. (unknown [10.28.34.39]) by maili.marvell.com (Postfix) with ESMTP id 904123F705A; Sun, 5 Feb 2023 10:39:01 -0800 (PST) From: Tomasz Duszynski To: , Thomas Monjalon , Tomasz Duszynski CC: , , Subject: [PATCH v2] bus: add platform bus Date: Sun, 5 Feb 2023 19:38:32 +0100 Message-ID: <20230205183832.842668-1-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: UM3dQgcEeluebfQPC6hhSeMG5NdMfTXv X-Proofpoint-ORIG-GUID: UM3dQgcEeluebfQPC6hhSeMG5NdMfTXv 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-02-05_06,2023-02-03_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 --- v2: - take a step back and do not add eal changes that simplify parsing sysfs string. That should be moved to a more generic library and submitted separately. - compile code conditionally based on VFIO_PRESENT MAINTAINERS | 4 + doc/guides/rel_notes/release_23_03.rst | 5 + drivers/bus/meson.build | 1 + drivers/bus/platform/bus_platform_driver.h | 192 +++++++ drivers/bus/platform/meson.build | 16 + drivers/bus/platform/platform.c | 632 +++++++++++++++++++++ drivers/bus/platform/platform_params.c | 74 +++ drivers/bus/platform/private.h | 48 ++ drivers/bus/platform/version.map | 10 + 9 files changed, 982 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 -- 2.34.1 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 6c42c27886..c7ff097c94 100644 --- a/doc/guides/rel_notes/release_23_03.rst +++ b/doc/guides/rel_notes/release_23_03.rst @@ -59,6 +59,11 @@ New Features * Added multi-process support. +* **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..c8c074f3e8 --- /dev/null +++ b/drivers/bus/platform/bus_platform_driver.h @@ -0,0 +1,192 @@ +/* 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) + +/** 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) + +#ifdef VFIO_PRESENT + +/** + * 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); + +/** + * 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); + +#else + +__rte_internal +static inline void +rte_platform_register(struct rte_platform_driver *pdrv __rte_unused) +{ +} + +__rte_internal +static inline void +rte_platform_unregister(struct rte_platform_driver *pdrv __rte_unused) +{ +} + +#endif /* VFIO_PRESENT */ + +#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..9f06327b77 --- /dev/null +++ b/drivers/bus/platform/platform.c @@ -0,0 +1,632 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifdef VFIO_PRESENT + +#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; + + dp = opendir(PLATFORM_BUS_DEVICES_PATH); + if (dp == 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 int +read_sysfs_string(const char *path, char *buf, size_t size) +{ + FILE *f; + char *p; + + f = fopen(path, "r"); + if (f == NULL) + return -errno; + + if (fgets(buf, size, f) == NULL) { + fclose(f); + return -ENODATA; + } + + fclose(f); + + p = strrchr(buf, '\n'); + if (p != NULL) + *p = '\0'; + + return 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 = read_sysfs_string(path, buf, sizeof(buf)); + 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); + +#endif /* VFIO_PRESENT */ diff --git a/drivers/bus/platform/platform_params.c b/drivers/bus/platform/platform_params.c new file mode 100644 index 0000000000..269683952a --- /dev/null +++ b/drivers/bus/platform/platform_params.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2023 Marvell. + */ + +#ifdef VFIO_PRESENT + +#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; +} + +#endif /* VFIO_PRESENT */ 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; +};