* [PATCH 0/2] add platform bus
@ 2023-01-25 10:38 Tomasz Duszynski
2023-01-25 10:38 ` [PATCH 1/2] lib: add helper to read strings from sysfs files Tomasz Duszynski
` (2 more replies)
0 siblings, 3 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-01-25 10:38 UTC (permalink / raw)
To: dev; +Cc: thomas, jerinj, stephen, chenbo.xia, Tomasz Duszynski
Platform bus is a bus under Linux which manages devices that do not have
any discovery-mechanism built in. Linux learns about platform devices
directly from device-tree during boot-up phase.
Afterwards if userspace wants to use some particular device driver being
usually a mixture of vdev/rawdev gets developed.
In order to simplify that introduce a DPDK platform bus which provides
auto-probe experience and separates a bus logic from the driver itself.
Now only devices which are backed-by vfio-platform kernel driver
are supported, though other options may be added if necessary.
Tomasz Duszynski (2):
lib: add helper to read strings from sysfs files
bus: add platform bus
MAINTAINERS | 4 +
app/test/test_eal_fs.c | 108 +++-
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 +
lib/eal/common/eal_filesystem.h | 6 +
lib/eal/unix/eal_filesystem.c | 24 +-
lib/eal/version.map | 1 +
13 files changed, 1053 insertions(+), 18 deletions(-)
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
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] lib: add helper to read strings from sysfs files
2023-01-25 10:38 [PATCH 0/2] add platform bus Tomasz Duszynski
@ 2023-01-25 10:38 ` 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
2 siblings, 0 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-01-25 10:38 UTC (permalink / raw)
To: dev; +Cc: thomas, jerinj, stephen, chenbo.xia, Tomasz Duszynski
Reading strings from sysfs files is a re-occurring pattern
hence add helper for doing that.
Signed-off-by: Tomasz Duszynski <tduszynski@marvell.com>
---
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;
--
2.34.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/2] bus: add platform bus
2023-01-25 10:38 [PATCH 0/2] add platform bus 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 ` Tomasz Duszynski
2023-02-05 18:38 ` [PATCH v2] " Tomasz Duszynski
2 siblings, 0 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-01-25 10:38 UTC (permalink / raw)
To: dev, Thomas Monjalon, Tomasz Duszynski; +Cc: jerinj, stephen, chenbo.xia
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>
---
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 <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/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 <stddef.h>
+#include <stdint.h>
+
+#include <dev_driver.h>
+#include <rte_common.h>
+#include <rte_dev.h>
+#include <rte_os.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)
+
+/**
+ * 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 <dirent.h>
+#include <inttypes.h>
+#include <linux/vfio.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"
+
+#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 <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 "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 <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
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2] bus: add platform bus
2023-01-25 10:38 [PATCH 0/2] add platform bus 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 ` Tomasz Duszynski
2023-02-06 14:28 ` David Marchand
2023-02-15 11:10 ` [PATCH v3] " Tomasz Duszynski
2 siblings, 2 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-02-05 18:38 UTC (permalink / raw)
To: dev, Thomas Monjalon, Tomasz Duszynski; +Cc: chenbo.xia, jerinj, stephen
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>
---
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
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 <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/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 <stddef.h>
+#include <stdint.h>
+
+#include <dev_driver.h>
+#include <rte_common.h>
+#include <rte_dev.h>
+#include <rte_os.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..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 <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"
+
+#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 <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 "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 <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
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2] bus: add platform bus
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
1 sibling, 1 reply; 13+ messages in thread
From: David Marchand @ 2023-02-06 14:28 UTC (permalink / raw)
To: Tomasz Duszynski; +Cc: dev, Thomas Monjalon, chenbo.xia, jerinj, stephen
On Sun, Feb 5, 2023 at 7:39 PM Tomasz Duszynski <tduszynski@marvell.com> wrote:
>
> 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>
> ---
> 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
I must be missing something, but in the form proposed in v2, this
won't work, see below.
[snip]
> 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
> +
VFIO_PRESENT is defined in rte_vfio.h and nothing else defines it.
So the rest of this file is a noop.
What did I miss?
So either you need to include <rte_vfio.h> first, or we could move the
VFIO_PRESENT detection stuff from this EAL header, to meson.
I did not look further into this patch, since this added code is
probably not tested (since not compiled).
--
David Marchand
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [EXT] Re: [PATCH v2] bus: add platform bus
2023-02-06 14:28 ` David Marchand
@ 2023-02-07 14:14 ` Tomasz Duszynski
0 siblings, 0 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-02-07 14:14 UTC (permalink / raw)
To: David Marchand
Cc: dev, Thomas Monjalon, chenbo.xia, Jerin Jacob Kollanukkaran, stephen
Hi David,
>-----Original Message-----
>From: David Marchand <david.marchand@redhat.com>
>Sent: Monday, February 6, 2023 3:29 PM
>To: Tomasz Duszynski <tduszynski@marvell.com>
>Cc: dev@dpdk.org; Thomas Monjalon <thomas@monjalon.net>; chenbo.xia@intel.com; Jerin Jacob
>Kollanukkaran <jerinj@marvell.com>; stephen@networkplumber.org
>Subject: [EXT] Re: [PATCH v2] bus: add platform bus
>
>External Email
>
>----------------------------------------------------------------------
>On Sun, Feb 5, 2023 at 7:39 PM Tomasz Duszynski <tduszynski@marvell.com> wrote:
>>
>> 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>
>> ---
>> 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
>
>I must be missing something, but in the form proposed in v2, this won't work, see below.
>
>[snip]
>
No you did not miss anything. Prior to submission I realized that if dpdk uses pretty
old kernel there might be no vfio uapi header present hence added that define.
Yeah complete no-brainer. Will fix that in v3.
>
>> 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
>> +
>
>VFIO_PRESENT is defined in rte_vfio.h and nothing else defines it.
>So the rest of this file is a noop.
>What did I miss?
>
>So either you need to include <rte_vfio.h> first, or we could move the VFIO_PRESENT detection stuff
>from this EAL header, to meson.
>
>
>I did not look further into this patch, since this added code is probably not tested (since not
>compiled).
>
>
>--
>David Marchand
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3] bus: add platform bus
2023-02-05 18:38 ` [PATCH v2] " Tomasz Duszynski
2023-02-06 14:28 ` David Marchand
@ 2023-02-15 11:10 ` Tomasz Duszynski
2023-02-20 10:51 ` Thomas Monjalon
2023-03-01 19:59 ` [PATCH v4] " Tomasz Duszynski
1 sibling, 2 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-02-15 11:10 UTC (permalink / raw)
To: dev, Thomas Monjalon, Tomasz Duszynski
Cc: chenbo.xia, jerinj, stephen, david.marchand
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>
---
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/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 | 635 +++++++++++++++++++++
drivers/bus/platform/platform_params.c | 75 +++
drivers/bus/platform/private.h | 48 ++
drivers/bus/platform/version.map | 10 +
9 files changed, 987 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 3495946d0f..7a28f8c2b2 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/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst
index ad23355e54..e3e2b84e65 100644
--- a/doc/guides/rel_notes/release_23_03.rst
+++ b/doc/guides/rel_notes/release_23_03.rst
@@ -151,6 +151,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..521e038c7b
--- /dev/null
+++ b/drivers/bus/platform/platform.c
@@ -0,0 +1,635 @@
+/* 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_bus", 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, ®_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 != 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_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..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
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v3] bus: add platform bus
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
1 sibling, 1 reply; 13+ messages in thread
From: Thomas Monjalon @ 2023-02-20 10:51 UTC (permalink / raw)
To: Tomasz Duszynski; +Cc: dev, chenbo.xia, jerinj, stephen, david.marchand
15/02/2023 12:10, Tomasz Duszynski:
> 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>
I would like to better understand how to use it.
Please could you add some doc in doc/guides/linux_gsg/ ?
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [EXT] Re: [PATCH v3] bus: add platform bus
2023-02-20 10:51 ` Thomas Monjalon
@ 2023-02-22 12:46 ` Tomasz Duszynski
0 siblings, 0 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-02-22 12:46 UTC (permalink / raw)
To: Thomas Monjalon
Cc: dev, chenbo.xia, Jerin Jacob Kollanukkaran, stephen, david.marchand
>-----Original Message-----
>From: Thomas Monjalon <thomas@monjalon.net>
>Sent: Monday, February 20, 2023 11:52 AM
>To: Tomasz Duszynski <tduszynski@marvell.com>
>Cc: dev@dpdk.org; chenbo.xia@intel.com; Jerin Jacob Kollanukkaran <jerinj@marvell.com>;
>stephen@networkplumber.org; david.marchand@redhat.com
>Subject: [EXT] Re: [PATCH v3] bus: add platform bus
>
>External Email
>
>----------------------------------------------------------------------
>15/02/2023 12:10, Tomasz Duszynski:
>> 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>
>
>I would like to better understand how to use it.
>Please could you add some doc in doc/guides/linux_gsg/ ?
>
>
Sure. I'll update linux_drivers.rst as, at least to me, is seems the most reasonable place.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v4] bus: add platform bus
2023-02-15 11:10 ` [PATCH v3] " Tomasz Duszynski
2023-02-20 10:51 ` Thomas Monjalon
@ 2023-03-01 19:59 ` Tomasz Duszynski
2023-03-09 12:42 ` Tomasz Duszynski
2023-03-10 16:44 ` Thomas Monjalon
1 sibling, 2 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-03-01 19:59 UTC (permalink / raw)
To: dev, Thomas Monjalon, Tomasz Duszynski
Cc: chenbo.xia, jerinj, stephen, david.marchand
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, ®_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
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [PATCH v4] bus: add platform bus
2023-03-01 19:59 ` [PATCH v4] " Tomasz Duszynski
@ 2023-03-09 12:42 ` Tomasz Duszynski
2023-03-10 16:44 ` Thomas Monjalon
1 sibling, 0 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-03-09 12:42 UTC (permalink / raw)
To: Tomasz Duszynski, dev, Thomas Monjalon
Cc: chenbo.xia, Jerin Jacob Kollanukkaran, stephen, david.marchand
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, ®_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
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v4] bus: add platform bus
2023-03-01 19:59 ` [PATCH v4] " Tomasz Duszynski
2023-03-09 12:42 ` Tomasz Duszynski
@ 2023-03-10 16:44 ` Thomas Monjalon
2023-03-13 7:08 ` [EXT] " Tomasz Duszynski
1 sibling, 1 reply; 13+ messages in thread
From: Thomas Monjalon @ 2023-03-10 16:44 UTC (permalink / raw)
To: Tomasz Duszynski; +Cc: dev, chenbo.xia, jerinj, stephen, david.marchand
01/03/2023 20:59, Tomasz Duszynski:
> 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>
Applied with minor doc improvements, thanks.
^ permalink raw reply [flat|nested] 13+ messages in thread
* RE: [EXT] Re: [PATCH v4] bus: add platform bus
2023-03-10 16:44 ` Thomas Monjalon
@ 2023-03-13 7:08 ` Tomasz Duszynski
0 siblings, 0 replies; 13+ messages in thread
From: Tomasz Duszynski @ 2023-03-13 7:08 UTC (permalink / raw)
To: Thomas Monjalon
Cc: dev, chenbo.xia, Jerin Jacob Kollanukkaran, stephen, david.marchand
>-----Original Message-----
>From: Thomas Monjalon <thomas@monjalon.net>
>Sent: Friday, March 10, 2023 5:45 PM
>To: Tomasz Duszynski <tduszynski@marvell.com>
>Cc: dev@dpdk.org; chenbo.xia@intel.com; Jerin Jacob Kollanukkaran <jerinj@marvell.com>;
>stephen@networkplumber.org; david.marchand@redhat.com
>Subject: [EXT] Re: [PATCH v4] bus: add platform bus
>
>External Email
>
>----------------------------------------------------------------------
>01/03/2023 20:59, Tomasz Duszynski:
>> 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>
>
>Applied with minor doc improvements, thanks.
>
>
Thanks!
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2023-03-13 7:08 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-25 10:38 [PATCH 0/2] add platform bus 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
2023-03-10 16:44 ` Thomas Monjalon
2023-03-13 7:08 ` [EXT] " Tomasz Duszynski
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).