DPDK patches and discussions
 help / color / mirror / Atom feed
From: Tomasz Duszynski <tduszynski@marvell.com>
To: Tomasz Duszynski <tduszynski@marvell.com>,
	"dev@dpdk.org" <dev@dpdk.org>,
	 Thomas Monjalon <thomas@monjalon.net>
Cc: "chenbo.xia@intel.com" <chenbo.xia@intel.com>,
	Jerin Jacob Kollanukkaran <jerinj@marvell.com>,
	"stephen@networkplumber.org" <stephen@networkplumber.org>,
	"david.marchand@redhat.com" <david.marchand@redhat.com>
Subject: RE: [PATCH v4] bus: add platform bus
Date: Thu, 9 Mar 2023 12:42:13 +0000	[thread overview]
Message-ID: <DM4PR18MB4368BDBC8395C57E105E0714D2B59@DM4PR18MB4368.namprd18.prod.outlook.com> (raw)
In-Reply-To: <20230301195912.3835720-1-tduszynski@marvell.com>

Hi,

A gentle reminder, any other comments regarding this change?

>-----Original Message-----
>From: Tomasz Duszynski <tduszynski@marvell.com>
>Sent: Wednesday, March 1, 2023 8:59 PM
>To: dev@dpdk.org; Thomas Monjalon <thomas@monjalon.net>; Tomasz Duszynski <tduszynski@marvell.com>
>Cc: chenbo.xia@intel.com; Jerin Jacob Kollanukkaran <jerinj@marvell.com>;
>stephen@networkplumber.org; david.marchand@redhat.com
>Subject: [PATCH v4] bus: add platform bus
>
>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 <tduszynski@marvell.com>
>---
>v4:
>- add more documentation
>- address failing compilation on systems running pre vfio-platform kernels
>v3:
>- fix device blocking
>- fix conditional compilation based on VFIO_PRESENT
>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/linux_gsg/linux_drivers.rst     |  45 ++
> doc/guides/rel_notes/release_23_03.rst     |   5 +
> drivers/bus/meson.build                    |   1 +
> drivers/bus/platform/bus_platform_driver.h | 193 ++++++
> drivers/bus/platform/meson.build           |  16 +
> drivers/bus/platform/platform.c            | 645 +++++++++++++++++++++
> drivers/bus/platform/platform_params.c     |  75 +++
> drivers/bus/platform/private.h             |  48 ++
> drivers/bus/platform/version.map           |  10 +
> 10 files changed, 1042 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 ffbf91296e..7aea426b53 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 <tduszynski@marvell.com>
>+F: drivers/bus/platform
>+
> VMBUS bus driver
> M: Long Li <longli@microsoft.com>
> F: drivers/bus/vmbus/
>diff --git a/doc/guides/linux_gsg/linux_drivers.rst b/doc/guides/linux_gsg/linux_drivers.rst
>index 2f3f079aab..5e8a3ae810 100644
>--- a/doc/guides/linux_gsg/linux_drivers.rst
>+++ b/doc/guides/linux_gsg/linux_drivers.rst
>@@ -296,6 +296,51 @@ If ``CONFIG_VFIO_NOIOMMU`` is not enabled in the kernel configuration,  VFIO
>driver will not support the no-IOMMU mode,  and other alternatives (such as UIO drivers) will have
>to be used.
>
>+VFIO Platform
>+-------------
>+
>+VFIO Platform is a kernel driver that extends capabilities of VFIO by
>+adding support for platform devices that reside behind an IOMMU. Linux
>+usually learns about platform devices directly from device tree during boot-up phase.
>+Unlike, for example, PCI devices which have necessary information built-in.
>+
>+To make use of VFIO platform, the ``vfio-platform`` module must be loaded first:
>+
>+.. code-block:: console
>+
>+   sudo modprobe vfio-platform
>+
>+.. note::
>+
>+   By default ``vfio-platform`` assumes that platform device has dedicated reset
>+   driver. If such driver is missing or device does not require one this
>+   option can be turned off by setting ``reset_required=0`` module parameter.
>+
>+Afterwards platform device needs to be bound to ``vfio-platform``. This
>+is standard procedure requiring two steps. First ``driver_override``,
>+which is available inside platform device directory, needs to be set to
>+``vfio-platform``:
>+
>+.. code-block:: console
>+
>+   sudo echo vfio-platform >
>+ /sys/bus/platform/devices/DEV/driver_override
>+
>+Next ``DEV`` device must be bound to ``vfio-platform`` driver:
>+
>+.. code-block:: console
>+
>+   sudo echo DEV > /sys/bus/platform/drivers/vfio-platform/bind
>+
>+On application startup DPDK platform bus driver scans
>+``/sys/bus/platform/devices`` searching for devices that have
>+``driver`` symbolic link pointing to ``vfio-platform`` driver. Finally,
>+scanned devices are matched against available PMDs. Matching is
>+successful if either PMD name or PMD alias matches kernel driver name or PMD name matches platform
>device name, all in that order.
>+
>+VFIO Platform depends on ARM/ARM64 and is usually enabled on
>+distributions running on these systems. Consult your distributions
>+documentation to make sure that is the case.
>+
> .. _bifurcated_driver:
>
> Bifurcated Driver
>diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
>index 49c18617a5..4bde86165f 100644
>--- a/doc/guides/rel_notes/release_23_03.rst
>+++ b/doc/guides/rel_notes/release_23_03.rst
>@@ -197,6 +197,11 @@ New Features
>   * Added support to capture packets at each graph node with packet metadata and
>     node name.
>
>+* **Added platform bus support.**
>+
>+  A platform bus provides a way to use Linux platform devices which
>+ are compatible with vfio-platform kernel driver.
>+
>
> Removed Items
> -------------
>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..a7445f77de
>--- /dev/null
>+++ b/drivers/bus/platform/bus_platform_driver.h
>@@ -0,0 +1,193 @@
>+/* 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 <stddef.h>
>+#include <stdint.h>
>+
>+#include <dev_driver.h>
>+#include <rte_common.h>
>+#include <rte_dev.h>
>+#include <rte_os.h>
>+#include <rte_vfio.h>
>+
>+/* 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..536d9524c6
>--- /dev/null
>+++ b/drivers/bus/platform/platform.c
>@@ -0,0 +1,645 @@
>+/* SPDX-License-Identifier: BSD-3-Clause
>+ * Copyright(C) 2023 Marvell.
>+ */
>+
>+#include <dirent.h>
>+#include <inttypes.h>
>+#include <stdlib.h>
>+#include <string.h>
>+#include <sys/ioctl.h>
>+#include <sys/mman.h>
>+#include <sys/queue.h>
>+#include <unistd.h>
>+
>+#include <bus_driver.h>
>+#include <bus_platform_driver.h>
>+#include <eal_filesystem.h>
>+#include <rte_bus.h>
>+#include <rte_devargs.h>
>+#include <rte_errno.h>
>+#include <rte_log.h>
>+#include <rte_memory.h>
>+#include <rte_string_fns.h>
>+#include <rte_vfio.h>
>+
>+#include "private.h"
>+
>+#ifdef VFIO_PRESENT
>+
>+#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_SHARED, 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, &reg_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;
>+	}
>+
>+	/* This is an extra check to confirm that platform device was initialized
>+	 * by a kernel vfio-platform driver. On kernels that predate vfio-platform
>+	 * driver this flag obviously does not exist. In such scenarios this
>+	 * check needs to be removed otherwise compilation fails.
>+	 *
>+	 * Now, on such old kernels code will never reach here because
>+	 * there is another check much earlier which verifies whether
>+	 * device has been bound to vfio-platform driver.
>+	 */
>+#ifdef VFIO_DEVICE_FLAGS_PLATFORM
>+	if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
>+		PLATFORM_LOG(ERR, "device not backed by vfio-platform\n");
>+		ret = -ENOTSUP;
>+		goto out;
>+	}
>+#endif
>+
>+	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 != NULL) {
>+		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 = { };
>+	struct rte_platform_driver *pdrv;
>+	const char **out = addr;
>+
>+	rte_strscpy(pdev.name, name, sizeof(pdev.name));
>+
>+	FOREACH_DRIVER_ON_PLATFORM_BUS(pdrv) {
>+		if (driver_match_device(pdrv, &pdev))
>+			break;
>+	}
>+
>+	if (pdrv != NULL && addr != NULL)
>+		*out = name;
>+
>+	return pdrv != NULL ? 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, 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..edc246b53e
>--- /dev/null
>+++ b/drivers/bus/platform/platform_params.c
>@@ -0,0 +1,75 @@
>+/* SPDX-License-Identifier: BSD-3-Clause
>+ * Copyright(C) 2023 Marvell.
>+ */
>+
>+#include <string.h>
>+#include <errno.h>
>+
>+#include <rte_bus.h>
>+#include <rte_common.h>
>+#include <rte_dev.h>
>+#include <rte_errno.h>
>+#include <rte_kvargs.h>
>+#include <rte_vfio.h>
>+
>+#include "bus_platform_driver.h"
>+#include "private.h"
>+
>+#ifdef VFIO_PRESENT
>+
>+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 <bus_driver.h>
>+#include <rte_bus.h>
>+#include <rte_common.h>
>+#include <rte_dev.h>
>+#include <rte_log.h>
>+#include <rte_os.h>
>+
>+#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;
>+};
>--
>2.34.1


  reply	other threads:[~2023-03-09 12:42 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-01-25 10:38 [PATCH 0/2] " Tomasz Duszynski
2023-01-25 10:38 ` [PATCH 1/2] lib: add helper to read strings from sysfs files Tomasz Duszynski
2023-01-25 10:38 ` [PATCH 2/2] bus: add platform bus Tomasz Duszynski
2023-02-05 18:38 ` [PATCH v2] " Tomasz Duszynski
2023-02-06 14:28   ` David Marchand
2023-02-07 14:14     ` [EXT] " Tomasz Duszynski
2023-02-15 11:10   ` [PATCH v3] " Tomasz Duszynski
2023-02-20 10:51     ` Thomas Monjalon
2023-02-22 12:46       ` [EXT] " Tomasz Duszynski
2023-03-01 19:59     ` [PATCH v4] " Tomasz Duszynski
2023-03-09 12:42       ` Tomasz Duszynski [this message]
2023-03-10 16:44       ` Thomas Monjalon
2023-03-13  7:08         ` [EXT] " Tomasz Duszynski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DM4PR18MB4368BDBC8395C57E105E0714D2B59@DM4PR18MB4368.namprd18.prod.outlook.com \
    --to=tduszynski@marvell.com \
    --cc=chenbo.xia@intel.com \
    --cc=david.marchand@redhat.com \
    --cc=dev@dpdk.org \
    --cc=jerinj@marvell.com \
    --cc=stephen@networkplumber.org \
    --cc=thomas@monjalon.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).