xref: /dpdk/drivers/bus/vdev/vdev.c (revision 1bd1ab6fd010837773473d821f9284369b37264c)
127d8b826SJan Viktorin /* SPDX-License-Identifier: BSD-3-Clause
2d4a586d2SJianfeng Tan  * Copyright(c) 2016 RehiveTech. All rights reserved.
3d4a586d2SJianfeng Tan  */
4d4a586d2SJianfeng Tan 
5d4a586d2SJianfeng Tan #include <string.h>
6d4a586d2SJianfeng Tan #include <inttypes.h>
7d4a586d2SJianfeng Tan #include <stdio.h>
8d4a586d2SJianfeng Tan #include <stdlib.h>
9d4a586d2SJianfeng Tan #include <stdint.h>
10d4a586d2SJianfeng Tan #include <stdbool.h>
11d4a586d2SJianfeng Tan #include <sys/queue.h>
12d4a586d2SJianfeng Tan 
13d4a586d2SJianfeng Tan #include <rte_eal.h>
141acb7f54SDavid Marchand #include <dev_driver.h>
15a04322f6SDavid Marchand #include <bus_driver.h>
16d4a586d2SJianfeng Tan #include <rte_common.h>
17d4a586d2SJianfeng Tan #include <rte_devargs.h>
18d4a586d2SJianfeng Tan #include <rte_memory.h>
193b792ed9SThomas Monjalon #include <rte_tailq.h>
203b792ed9SThomas Monjalon #include <rte_spinlock.h>
21cdb068f0SJianfeng Tan #include <rte_string_fns.h>
22d4a586d2SJianfeng Tan #include <rte_errno.h>
23d4a586d2SJianfeng Tan 
244851ef2bSDavid Marchand #include "bus_vdev_driver.h"
25d22fcb22SJianfeng Tan #include "vdev_logs.h"
26ac91bc49SGaetan Rivet #include "vdev_private.h"
27d22fcb22SJianfeng Tan 
28cdb068f0SJianfeng Tan #define VDEV_MP_KEY	"bus_vdev_mp"
29cdb068f0SJianfeng Tan 
30d4a586d2SJianfeng Tan /* Forward declare to access virtual bus name */
31d4a586d2SJianfeng Tan static struct rte_bus rte_vdev_bus;
32d4a586d2SJianfeng Tan 
33d4a586d2SJianfeng Tan 
344851ef2bSDavid Marchand static TAILQ_HEAD(, rte_vdev_device) vdev_device_list =
35d4a586d2SJianfeng Tan 	TAILQ_HEAD_INITIALIZER(vdev_device_list);
36f14b264fSThomas Monjalon /* The lock needs to be recursive because a vdev can manage another vdev. */
37f14b264fSThomas Monjalon static rte_spinlock_recursive_t vdev_device_list_lock =
38f14b264fSThomas Monjalon 	RTE_SPINLOCK_RECURSIVE_INITIALIZER;
3935f46283SJianfeng Tan 
404851ef2bSDavid Marchand static TAILQ_HEAD(, rte_vdev_driver) vdev_driver_list =
41d4a586d2SJianfeng Tan 	TAILQ_HEAD_INITIALIZER(vdev_driver_list);
42d4a586d2SJianfeng Tan 
433b792ed9SThomas Monjalon struct vdev_custom_scan {
443b792ed9SThomas Monjalon 	TAILQ_ENTRY(vdev_custom_scan) next;
453b792ed9SThomas Monjalon 	rte_vdev_scan_callback callback;
463b792ed9SThomas Monjalon 	void *user_arg;
473b792ed9SThomas Monjalon };
483b792ed9SThomas Monjalon TAILQ_HEAD(vdev_custom_scans, vdev_custom_scan);
493b792ed9SThomas Monjalon static struct vdev_custom_scans vdev_custom_scans =
503b792ed9SThomas Monjalon 	TAILQ_HEAD_INITIALIZER(vdev_custom_scans);
513b792ed9SThomas Monjalon static rte_spinlock_t vdev_custom_scan_lock = RTE_SPINLOCK_INITIALIZER;
523b792ed9SThomas Monjalon 
53d4a586d2SJianfeng Tan /* register a driver */
54d4a586d2SJianfeng Tan void
55d4a586d2SJianfeng Tan rte_vdev_register(struct rte_vdev_driver *driver)
56d4a586d2SJianfeng Tan {
57d4a586d2SJianfeng Tan 	TAILQ_INSERT_TAIL(&vdev_driver_list, driver, next);
58d4a586d2SJianfeng Tan }
59d4a586d2SJianfeng Tan 
60d4a586d2SJianfeng Tan /* unregister a driver */
61d4a586d2SJianfeng Tan void
62d4a586d2SJianfeng Tan rte_vdev_unregister(struct rte_vdev_driver *driver)
63d4a586d2SJianfeng Tan {
64d4a586d2SJianfeng Tan 	TAILQ_REMOVE(&vdev_driver_list, driver, next);
65d4a586d2SJianfeng Tan }
66d4a586d2SJianfeng Tan 
673b792ed9SThomas Monjalon int
683b792ed9SThomas Monjalon rte_vdev_add_custom_scan(rte_vdev_scan_callback callback, void *user_arg)
693b792ed9SThomas Monjalon {
703b792ed9SThomas Monjalon 	struct vdev_custom_scan *custom_scan;
713b792ed9SThomas Monjalon 
723b792ed9SThomas Monjalon 	rte_spinlock_lock(&vdev_custom_scan_lock);
733b792ed9SThomas Monjalon 
743b792ed9SThomas Monjalon 	/* check if already registered */
753b792ed9SThomas Monjalon 	TAILQ_FOREACH(custom_scan, &vdev_custom_scans, next) {
763b792ed9SThomas Monjalon 		if (custom_scan->callback == callback &&
773b792ed9SThomas Monjalon 				custom_scan->user_arg == user_arg)
783b792ed9SThomas Monjalon 			break;
793b792ed9SThomas Monjalon 	}
803b792ed9SThomas Monjalon 
813b792ed9SThomas Monjalon 	if (custom_scan == NULL) {
823b792ed9SThomas Monjalon 		custom_scan = malloc(sizeof(struct vdev_custom_scan));
833b792ed9SThomas Monjalon 		if (custom_scan != NULL) {
843b792ed9SThomas Monjalon 			custom_scan->callback = callback;
853b792ed9SThomas Monjalon 			custom_scan->user_arg = user_arg;
863b792ed9SThomas Monjalon 			TAILQ_INSERT_TAIL(&vdev_custom_scans, custom_scan, next);
873b792ed9SThomas Monjalon 		}
883b792ed9SThomas Monjalon 	}
893b792ed9SThomas Monjalon 
903b792ed9SThomas Monjalon 	rte_spinlock_unlock(&vdev_custom_scan_lock);
913b792ed9SThomas Monjalon 
923b792ed9SThomas Monjalon 	return (custom_scan == NULL) ? -1 : 0;
933b792ed9SThomas Monjalon }
943b792ed9SThomas Monjalon 
953b792ed9SThomas Monjalon int
963b792ed9SThomas Monjalon rte_vdev_remove_custom_scan(rte_vdev_scan_callback callback, void *user_arg)
973b792ed9SThomas Monjalon {
983b792ed9SThomas Monjalon 	struct vdev_custom_scan *custom_scan, *tmp_scan;
993b792ed9SThomas Monjalon 
1003b792ed9SThomas Monjalon 	rte_spinlock_lock(&vdev_custom_scan_lock);
101f1f6ebc0SWilliam Tu 	RTE_TAILQ_FOREACH_SAFE(custom_scan, &vdev_custom_scans, next,
102f1f6ebc0SWilliam Tu 				tmp_scan) {
1033b792ed9SThomas Monjalon 		if (custom_scan->callback != callback ||
1043b792ed9SThomas Monjalon 				(custom_scan->user_arg != (void *)-1 &&
1053b792ed9SThomas Monjalon 				custom_scan->user_arg != user_arg))
1063b792ed9SThomas Monjalon 			continue;
1073b792ed9SThomas Monjalon 		TAILQ_REMOVE(&vdev_custom_scans, custom_scan, next);
1083b792ed9SThomas Monjalon 		free(custom_scan);
1093b792ed9SThomas Monjalon 	}
1103b792ed9SThomas Monjalon 	rte_spinlock_unlock(&vdev_custom_scan_lock);
1113b792ed9SThomas Monjalon 
1123b792ed9SThomas Monjalon 	return 0;
1133b792ed9SThomas Monjalon }
1143b792ed9SThomas Monjalon 
115d4a586d2SJianfeng Tan static int
116d4a586d2SJianfeng Tan vdev_parse(const char *name, void *addr)
117d4a586d2SJianfeng Tan {
118d4a586d2SJianfeng Tan 	struct rte_vdev_driver **out = addr;
119d4a586d2SJianfeng Tan 	struct rte_vdev_driver *driver = NULL;
120d4a586d2SJianfeng Tan 
121d4a586d2SJianfeng Tan 	TAILQ_FOREACH(driver, &vdev_driver_list, next) {
122d4a586d2SJianfeng Tan 		if (strncmp(driver->driver.name, name,
123d4a586d2SJianfeng Tan 			    strlen(driver->driver.name)) == 0)
124d4a586d2SJianfeng Tan 			break;
125d4a586d2SJianfeng Tan 		if (driver->driver.alias &&
126d4a586d2SJianfeng Tan 		    strncmp(driver->driver.alias, name,
127d4a586d2SJianfeng Tan 			    strlen(driver->driver.alias)) == 0)
128d4a586d2SJianfeng Tan 			break;
129d4a586d2SJianfeng Tan 	}
130d4a586d2SJianfeng Tan 	if (driver != NULL &&
131d4a586d2SJianfeng Tan 	    addr != NULL)
132d4a586d2SJianfeng Tan 		*out = driver;
133d4a586d2SJianfeng Tan 	return driver == NULL;
134d4a586d2SJianfeng Tan }
135d4a586d2SJianfeng Tan 
136d4a586d2SJianfeng Tan static int
1376a2288edSMaxime Coquelin vdev_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
1386a2288edSMaxime Coquelin {
1396a2288edSMaxime Coquelin 	struct rte_vdev_device *vdev = RTE_DEV_TO_VDEV(dev);
1406a2288edSMaxime Coquelin 	const struct rte_vdev_driver *driver;
1416a2288edSMaxime Coquelin 
1426a2288edSMaxime Coquelin 	if (!vdev) {
1436a2288edSMaxime Coquelin 		rte_errno = EINVAL;
1446a2288edSMaxime Coquelin 		return -1;
1456a2288edSMaxime Coquelin 	}
1466a2288edSMaxime Coquelin 
1476a2288edSMaxime Coquelin 	if (!vdev->device.driver) {
1486a2288edSMaxime Coquelin 		VDEV_LOG(DEBUG, "no driver attach to device %s", dev->name);
1496a2288edSMaxime Coquelin 		return 1;
1506a2288edSMaxime Coquelin 	}
1516a2288edSMaxime Coquelin 
1526a2288edSMaxime Coquelin 	driver = container_of(vdev->device.driver, const struct rte_vdev_driver,
1536a2288edSMaxime Coquelin 			driver);
1546a2288edSMaxime Coquelin 
1556a2288edSMaxime Coquelin 	if (driver->dma_map)
1566a2288edSMaxime Coquelin 		return driver->dma_map(vdev, addr, iova, len);
1576a2288edSMaxime Coquelin 
1586a2288edSMaxime Coquelin 	return 0;
1596a2288edSMaxime Coquelin }
1606a2288edSMaxime Coquelin 
1616a2288edSMaxime Coquelin static int
1626a2288edSMaxime Coquelin vdev_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
1636a2288edSMaxime Coquelin {
1646a2288edSMaxime Coquelin 	struct rte_vdev_device *vdev = RTE_DEV_TO_VDEV(dev);
1656a2288edSMaxime Coquelin 	const struct rte_vdev_driver *driver;
1666a2288edSMaxime Coquelin 
1676a2288edSMaxime Coquelin 	if (!vdev) {
1686a2288edSMaxime Coquelin 		rte_errno = EINVAL;
1696a2288edSMaxime Coquelin 		return -1;
1706a2288edSMaxime Coquelin 	}
1716a2288edSMaxime Coquelin 
1726a2288edSMaxime Coquelin 	if (!vdev->device.driver) {
1736a2288edSMaxime Coquelin 		VDEV_LOG(DEBUG, "no driver attach to device %s", dev->name);
1746a2288edSMaxime Coquelin 		return 1;
1756a2288edSMaxime Coquelin 	}
1766a2288edSMaxime Coquelin 
1776a2288edSMaxime Coquelin 	driver = container_of(vdev->device.driver, const struct rte_vdev_driver,
1786a2288edSMaxime Coquelin 			driver);
1796a2288edSMaxime Coquelin 
1806a2288edSMaxime Coquelin 	if (driver->dma_unmap)
1816a2288edSMaxime Coquelin 		return driver->dma_unmap(vdev, addr, iova, len);
1826a2288edSMaxime Coquelin 
1836a2288edSMaxime Coquelin 	return 0;
1846a2288edSMaxime Coquelin }
1856a2288edSMaxime Coquelin 
1866a2288edSMaxime Coquelin static int
187d4a586d2SJianfeng Tan vdev_probe_all_drivers(struct rte_vdev_device *dev)
188d4a586d2SJianfeng Tan {
189d4a586d2SJianfeng Tan 	const char *name;
190d4a586d2SJianfeng Tan 	struct rte_vdev_driver *driver;
1918d935fffSMaxime Coquelin 	enum rte_iova_mode iova_mode;
192d4a586d2SJianfeng Tan 	int ret;
193d4a586d2SJianfeng Tan 
194e892fa59SRaslan Darawsheh 	if (rte_dev_is_probed(&dev->device))
195e892fa59SRaslan Darawsheh 		return -EEXIST;
196e892fa59SRaslan Darawsheh 
197d4a586d2SJianfeng Tan 	name = rte_vdev_device_name(dev);
1984169ed6eSThomas Monjalon 	VDEV_LOG(DEBUG, "Search driver to probe device %s", name);
199d4a586d2SJianfeng Tan 
200d4a586d2SJianfeng Tan 	if (vdev_parse(name, &driver))
201d4a586d2SJianfeng Tan 		return -1;
2028d935fffSMaxime Coquelin 
2038d935fffSMaxime Coquelin 	iova_mode = rte_eal_iova_mode();
2048d935fffSMaxime Coquelin 	if ((driver->drv_flags & RTE_VDEV_DRV_NEED_IOVA_AS_VA) && (iova_mode == RTE_IOVA_PA)) {
2058d935fffSMaxime Coquelin 		VDEV_LOG(ERR, "%s requires VA IOVA mode but current mode is PA, not initializing",
2068d935fffSMaxime Coquelin 				name);
2078d935fffSMaxime Coquelin 		return -1;
2088d935fffSMaxime Coquelin 	}
2098d935fffSMaxime Coquelin 
210d4a586d2SJianfeng Tan 	ret = driver->probe(dev);
211391797f0SThomas Monjalon 	if (ret == 0)
212391797f0SThomas Monjalon 		dev->device.driver = &driver->driver;
213d4a586d2SJianfeng Tan 	return ret;
214d4a586d2SJianfeng Tan }
215d4a586d2SJianfeng Tan 
21635f46283SJianfeng Tan /* The caller shall be responsible for thread-safe */
217d4a586d2SJianfeng Tan static struct rte_vdev_device *
218d4a586d2SJianfeng Tan find_vdev(const char *name)
219d4a586d2SJianfeng Tan {
220d4a586d2SJianfeng Tan 	struct rte_vdev_device *dev;
221d4a586d2SJianfeng Tan 
222d4a586d2SJianfeng Tan 	if (!name)
223d4a586d2SJianfeng Tan 		return NULL;
224d4a586d2SJianfeng Tan 
225d4a586d2SJianfeng Tan 	TAILQ_FOREACH(dev, &vdev_device_list, next) {
226d4a586d2SJianfeng Tan 		const char *devname = rte_vdev_device_name(dev);
227d4a586d2SJianfeng Tan 
228fada6963SNachiketa Prachanda 		if (!strcmp(devname, name))
229d4a586d2SJianfeng Tan 			return dev;
230d4a586d2SJianfeng Tan 	}
231d4a586d2SJianfeng Tan 
232d4a586d2SJianfeng Tan 	return NULL;
233d4a586d2SJianfeng Tan }
234d4a586d2SJianfeng Tan 
235d4a586d2SJianfeng Tan static struct rte_devargs *
236d4a586d2SJianfeng Tan alloc_devargs(const char *name, const char *args)
237d4a586d2SJianfeng Tan {
238d4a586d2SJianfeng Tan 	struct rte_devargs *devargs;
239d4a586d2SJianfeng Tan 	int ret;
240d4a586d2SJianfeng Tan 
241d4a586d2SJianfeng Tan 	devargs = calloc(1, sizeof(*devargs));
242d4a586d2SJianfeng Tan 	if (!devargs)
243d4a586d2SJianfeng Tan 		return NULL;
244d4a586d2SJianfeng Tan 
245d4a586d2SJianfeng Tan 	devargs->bus = &rte_vdev_bus;
246d4a586d2SJianfeng Tan 	if (args)
24764051bb1SXueming Li 		devargs->data = strdup(args);
248d4a586d2SJianfeng Tan 	else
24964051bb1SXueming Li 		devargs->data = strdup("");
25083f275faSChengwen Feng 	if (devargs->data == NULL) {
25183f275faSChengwen Feng 		free(devargs);
25283f275faSChengwen Feng 		return NULL;
25383f275faSChengwen Feng 	}
25464051bb1SXueming Li 	devargs->args = devargs->data;
255d4a586d2SJianfeng Tan 
256f9acaf84SBruce Richardson 	ret = strlcpy(devargs->name, name, sizeof(devargs->name));
257d4a586d2SJianfeng Tan 	if (ret < 0 || ret >= (int)sizeof(devargs->name)) {
25864051bb1SXueming Li 		rte_devargs_reset(devargs);
259d4a586d2SJianfeng Tan 		free(devargs);
260d4a586d2SJianfeng Tan 		return NULL;
261d4a586d2SJianfeng Tan 	}
262d4a586d2SJianfeng Tan 
263d4a586d2SJianfeng Tan 	return devargs;
264d4a586d2SJianfeng Tan }
265d4a586d2SJianfeng Tan 
26635f46283SJianfeng Tan static int
267f5b2eff0SQi Zhang insert_vdev(const char *name, const char *args,
268f5b2eff0SQi Zhang 		struct rte_vdev_device **p_dev,
269f5b2eff0SQi Zhang 		bool init)
270d4a586d2SJianfeng Tan {
271d4a586d2SJianfeng Tan 	struct rte_vdev_device *dev;
272d4a586d2SJianfeng Tan 	struct rte_devargs *devargs;
273d4a586d2SJianfeng Tan 	int ret;
274d4a586d2SJianfeng Tan 
275d4a586d2SJianfeng Tan 	if (name == NULL)
276d4a586d2SJianfeng Tan 		return -EINVAL;
277d4a586d2SJianfeng Tan 
278d4a586d2SJianfeng Tan 	devargs = alloc_devargs(name, args);
27966666283SMingjin Ye 
280d4a586d2SJianfeng Tan 	if (!devargs)
281d4a586d2SJianfeng Tan 		return -ENOMEM;
282d4a586d2SJianfeng Tan 
283d4a586d2SJianfeng Tan 	dev = calloc(1, sizeof(*dev));
284d4a586d2SJianfeng Tan 	if (!dev) {
285d4a586d2SJianfeng Tan 		ret = -ENOMEM;
286d4a586d2SJianfeng Tan 		goto fail;
287d4a586d2SJianfeng Tan 	}
288d4a586d2SJianfeng Tan 
2899ffe2f4eSQi Zhang 	dev->device.bus = &rte_vdev_bus;
290d4a586d2SJianfeng Tan 	dev->device.numa_node = SOCKET_ID_ANY;
291d4a586d2SJianfeng Tan 
29235f46283SJianfeng Tan 	if (find_vdev(name)) {
293e9d159c3SThomas Monjalon 		/*
294e9d159c3SThomas Monjalon 		 * A vdev is expected to have only one port.
295e9d159c3SThomas Monjalon 		 * So there is no reason to try probing again,
296e9d159c3SThomas Monjalon 		 * even with new arguments.
297e9d159c3SThomas Monjalon 		 */
29835f46283SJianfeng Tan 		ret = -EEXIST;
299d4a586d2SJianfeng Tan 		goto fail;
300d4a586d2SJianfeng Tan 	}
301d4a586d2SJianfeng Tan 
302f5b2eff0SQi Zhang 	if (init)
303c7ad7754SThomas Monjalon 		rte_devargs_insert(&devargs);
304c7ad7754SThomas Monjalon 	dev->device.devargs = devargs;
305*1bd1ab6fSMingjin Ye 	dev->device.name = devargs->name;
306c7ad7754SThomas Monjalon 	TAILQ_INSERT_TAIL(&vdev_device_list, dev, next);
307d4a586d2SJianfeng Tan 
30835f46283SJianfeng Tan 	if (p_dev)
30935f46283SJianfeng Tan 		*p_dev = dev;
310d4a586d2SJianfeng Tan 
31135f46283SJianfeng Tan 	return 0;
312d4a586d2SJianfeng Tan fail:
31364051bb1SXueming Li 	rte_devargs_reset(devargs);
314d4a586d2SJianfeng Tan 	free(devargs);
315d4a586d2SJianfeng Tan 	free(dev);
316d4a586d2SJianfeng Tan 	return ret;
317d4a586d2SJianfeng Tan }
318d4a586d2SJianfeng Tan 
31935f46283SJianfeng Tan int
32035f46283SJianfeng Tan rte_vdev_init(const char *name, const char *args)
32135f46283SJianfeng Tan {
32235f46283SJianfeng Tan 	struct rte_vdev_device *dev;
32335f46283SJianfeng Tan 	int ret;
32435f46283SJianfeng Tan 
325f14b264fSThomas Monjalon 	rte_spinlock_recursive_lock(&vdev_device_list_lock);
326f5b2eff0SQi Zhang 	ret = insert_vdev(name, args, &dev, true);
32735f46283SJianfeng Tan 	if (ret == 0) {
32835f46283SJianfeng Tan 		ret = vdev_probe_all_drivers(dev);
32935f46283SJianfeng Tan 		if (ret) {
33035f46283SJianfeng Tan 			if (ret > 0)
331999951c8SStephen Hemminger 				VDEV_LOG(ERR, "no driver found for %s", name);
33235f46283SJianfeng Tan 			/* If fails, remove it from vdev list */
33335f46283SJianfeng Tan 			TAILQ_REMOVE(&vdev_device_list, dev, next);
3342effa126SThomas Monjalon 			rte_devargs_remove(dev->device.devargs);
33535f46283SJianfeng Tan 			free(dev);
33635f46283SJianfeng Tan 		}
33735f46283SJianfeng Tan 	}
338f14b264fSThomas Monjalon 	rte_spinlock_recursive_unlock(&vdev_device_list_lock);
33935f46283SJianfeng Tan 	return ret;
34035f46283SJianfeng Tan }
34135f46283SJianfeng Tan 
342d4a586d2SJianfeng Tan static int
343d4a586d2SJianfeng Tan vdev_remove_driver(struct rte_vdev_device *dev)
344d4a586d2SJianfeng Tan {
345d4a586d2SJianfeng Tan 	const char *name = rte_vdev_device_name(dev);
346d4a586d2SJianfeng Tan 	const struct rte_vdev_driver *driver;
347d4a586d2SJianfeng Tan 
348d4a586d2SJianfeng Tan 	if (!dev->device.driver) {
349999951c8SStephen Hemminger 		VDEV_LOG(DEBUG, "no driver attach to device %s", name);
350d4a586d2SJianfeng Tan 		return 1;
351d4a586d2SJianfeng Tan 	}
352d4a586d2SJianfeng Tan 
353d4a586d2SJianfeng Tan 	driver = container_of(dev->device.driver, const struct rte_vdev_driver,
354d4a586d2SJianfeng Tan 		driver);
355d4a586d2SJianfeng Tan 	return driver->remove(dev);
356d4a586d2SJianfeng Tan }
357d4a586d2SJianfeng Tan 
358d4a586d2SJianfeng Tan int
359d4a586d2SJianfeng Tan rte_vdev_uninit(const char *name)
360d4a586d2SJianfeng Tan {
361d4a586d2SJianfeng Tan 	struct rte_vdev_device *dev;
362d4a586d2SJianfeng Tan 	int ret;
363d4a586d2SJianfeng Tan 
364d4a586d2SJianfeng Tan 	if (name == NULL)
365d4a586d2SJianfeng Tan 		return -EINVAL;
366d4a586d2SJianfeng Tan 
367f14b264fSThomas Monjalon 	rte_spinlock_recursive_lock(&vdev_device_list_lock);
368d4a586d2SJianfeng Tan 
36935f46283SJianfeng Tan 	dev = find_vdev(name);
37035f46283SJianfeng Tan 	if (!dev) {
37135f46283SJianfeng Tan 		ret = -ENOENT;
37235f46283SJianfeng Tan 		goto unlock;
37335f46283SJianfeng Tan 	}
374d4a586d2SJianfeng Tan 
375d4a586d2SJianfeng Tan 	ret = vdev_remove_driver(dev);
376d4a586d2SJianfeng Tan 	if (ret)
37735f46283SJianfeng Tan 		goto unlock;
378d4a586d2SJianfeng Tan 
379d4a586d2SJianfeng Tan 	TAILQ_REMOVE(&vdev_device_list, dev, next);
3802effa126SThomas Monjalon 	rte_devargs_remove(dev->device.devargs);
381d4a586d2SJianfeng Tan 	free(dev);
38235f46283SJianfeng Tan 
38335f46283SJianfeng Tan unlock:
384f14b264fSThomas Monjalon 	rte_spinlock_recursive_unlock(&vdev_device_list_lock);
38535f46283SJianfeng Tan 	return ret;
386d4a586d2SJianfeng Tan }
387d4a586d2SJianfeng Tan 
388cdb068f0SJianfeng Tan struct vdev_param {
389cdb068f0SJianfeng Tan #define VDEV_SCAN_REQ	1
390cdb068f0SJianfeng Tan #define VDEV_SCAN_ONE	2
391cdb068f0SJianfeng Tan #define VDEV_SCAN_REP	3
392cdb068f0SJianfeng Tan 	int type;
393cdb068f0SJianfeng Tan 	int num;
394cdb068f0SJianfeng Tan 	char name[RTE_DEV_NAME_MAX_LEN];
395cdb068f0SJianfeng Tan };
396cdb068f0SJianfeng Tan 
397cdb068f0SJianfeng Tan static int vdev_plug(struct rte_device *dev);
398cdb068f0SJianfeng Tan 
399cdb068f0SJianfeng Tan /**
400cdb068f0SJianfeng Tan  * This function works as the action for both primary and secondary process
401cdb068f0SJianfeng Tan  * for static vdev discovery when a secondary process is booting.
402cdb068f0SJianfeng Tan  *
403cdb068f0SJianfeng Tan  * step 1, secondary process sends a sync request to ask for vdev in primary;
404cdb068f0SJianfeng Tan  * step 2, primary process receives the request, and send vdevs one by one;
405cdb068f0SJianfeng Tan  * step 3, primary process sends back reply, which indicates how many vdevs
406cdb068f0SJianfeng Tan  * are sent.
407cdb068f0SJianfeng Tan  */
408cdb068f0SJianfeng Tan static int
409cdb068f0SJianfeng Tan vdev_action(const struct rte_mp_msg *mp_msg, const void *peer)
410cdb068f0SJianfeng Tan {
411cdb068f0SJianfeng Tan 	struct rte_vdev_device *dev;
412cdb068f0SJianfeng Tan 	struct rte_mp_msg mp_resp;
413cdb068f0SJianfeng Tan 	struct vdev_param *ou = (struct vdev_param *)&mp_resp.param;
414cdb068f0SJianfeng Tan 	const struct vdev_param *in = (const struct vdev_param *)mp_msg->param;
415cdb068f0SJianfeng Tan 	const char *devname;
416cdb068f0SJianfeng Tan 	int num;
41723f1c424SQi Zhang 	int ret;
418cdb068f0SJianfeng Tan 
419cdb068f0SJianfeng Tan 	strlcpy(mp_resp.name, VDEV_MP_KEY, sizeof(mp_resp.name));
420cdb068f0SJianfeng Tan 	mp_resp.len_param = sizeof(*ou);
421cdb068f0SJianfeng Tan 	mp_resp.num_fds = 0;
422cdb068f0SJianfeng Tan 
423cdb068f0SJianfeng Tan 	switch (in->type) {
424cdb068f0SJianfeng Tan 	case VDEV_SCAN_REQ:
425cdb068f0SJianfeng Tan 		ou->type = VDEV_SCAN_ONE;
426cdb068f0SJianfeng Tan 		ou->num = 1;
427cdb068f0SJianfeng Tan 		num = 0;
428cdb068f0SJianfeng Tan 
429f14b264fSThomas Monjalon 		rte_spinlock_recursive_lock(&vdev_device_list_lock);
430cdb068f0SJianfeng Tan 		TAILQ_FOREACH(dev, &vdev_device_list, next) {
431cdb068f0SJianfeng Tan 			devname = rte_vdev_device_name(dev);
432cdb068f0SJianfeng Tan 			if (strlen(devname) == 0) {
433cdb068f0SJianfeng Tan 				VDEV_LOG(INFO, "vdev with no name is not sent");
434cdb068f0SJianfeng Tan 				continue;
435cdb068f0SJianfeng Tan 			}
436cdb068f0SJianfeng Tan 			VDEV_LOG(INFO, "send vdev, %s", devname);
437cdb068f0SJianfeng Tan 			strlcpy(ou->name, devname, RTE_DEV_NAME_MAX_LEN);
438cdb068f0SJianfeng Tan 			if (rte_mp_sendmsg(&mp_resp) < 0)
439cdb068f0SJianfeng Tan 				VDEV_LOG(ERR, "send vdev, %s, failed, %s",
440cdb068f0SJianfeng Tan 					 devname, strerror(rte_errno));
441cdb068f0SJianfeng Tan 			num++;
442cdb068f0SJianfeng Tan 		}
443f14b264fSThomas Monjalon 		rte_spinlock_recursive_unlock(&vdev_device_list_lock);
444cdb068f0SJianfeng Tan 
445cdb068f0SJianfeng Tan 		ou->type = VDEV_SCAN_REP;
446cdb068f0SJianfeng Tan 		ou->num = num;
447cdb068f0SJianfeng Tan 		if (rte_mp_reply(&mp_resp, peer) < 0)
448cdb068f0SJianfeng Tan 			VDEV_LOG(ERR, "Failed to reply a scan request");
449cdb068f0SJianfeng Tan 		break;
450cdb068f0SJianfeng Tan 	case VDEV_SCAN_ONE:
451cdb068f0SJianfeng Tan 		VDEV_LOG(INFO, "receive vdev, %s", in->name);
452f5b2eff0SQi Zhang 		ret = insert_vdev(in->name, NULL, NULL, false);
45323f1c424SQi Zhang 		if (ret == -EEXIST)
45423f1c424SQi Zhang 			VDEV_LOG(DEBUG, "device already exist, %s", in->name);
45523f1c424SQi Zhang 		else if (ret < 0)
456cdb068f0SJianfeng Tan 			VDEV_LOG(ERR, "failed to add vdev, %s", in->name);
457cdb068f0SJianfeng Tan 		break;
458cdb068f0SJianfeng Tan 	default:
459cdb068f0SJianfeng Tan 		VDEV_LOG(ERR, "vdev cannot recognize this message");
460cdb068f0SJianfeng Tan 	}
461cdb068f0SJianfeng Tan 
462cdb068f0SJianfeng Tan 	return 0;
463cdb068f0SJianfeng Tan }
464cdb068f0SJianfeng Tan 
465d4a586d2SJianfeng Tan static int
466d4a586d2SJianfeng Tan vdev_scan(void)
467d4a586d2SJianfeng Tan {
468d4a586d2SJianfeng Tan 	struct rte_vdev_device *dev;
469d4a586d2SJianfeng Tan 	struct rte_devargs *devargs;
4703b792ed9SThomas Monjalon 	struct vdev_custom_scan *custom_scan;
4713b792ed9SThomas Monjalon 
472cdb068f0SJianfeng Tan 	if (rte_mp_action_register(VDEV_MP_KEY, vdev_action) < 0 &&
473cdb068f0SJianfeng Tan 	    rte_errno != EEXIST) {
474edf73dd3SAnatoly Burakov 		/* for primary, unsupported IPC is not an error */
475edf73dd3SAnatoly Burakov 		if (rte_eal_process_type() == RTE_PROC_PRIMARY &&
476edf73dd3SAnatoly Burakov 				rte_errno == ENOTSUP)
477edf73dd3SAnatoly Burakov 			goto scan;
478cdb068f0SJianfeng Tan 		VDEV_LOG(ERR, "Failed to add vdev mp action");
479cdb068f0SJianfeng Tan 		return -1;
480cdb068f0SJianfeng Tan 	}
481cdb068f0SJianfeng Tan 
482cdb068f0SJianfeng Tan 	if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
483cdb068f0SJianfeng Tan 		struct rte_mp_msg mp_req, *mp_rep;
484cdb068f0SJianfeng Tan 		struct rte_mp_reply mp_reply;
485cdb068f0SJianfeng Tan 		struct timespec ts = {.tv_sec = 5, .tv_nsec = 0};
486cdb068f0SJianfeng Tan 		struct vdev_param *req = (struct vdev_param *)mp_req.param;
487cdb068f0SJianfeng Tan 		struct vdev_param *resp;
488cdb068f0SJianfeng Tan 
489cdb068f0SJianfeng Tan 		strlcpy(mp_req.name, VDEV_MP_KEY, sizeof(mp_req.name));
490cdb068f0SJianfeng Tan 		mp_req.len_param = sizeof(*req);
491cdb068f0SJianfeng Tan 		mp_req.num_fds = 0;
492cdb068f0SJianfeng Tan 		req->type = VDEV_SCAN_REQ;
493cdb068f0SJianfeng Tan 		if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) == 0 &&
494cdb068f0SJianfeng Tan 		    mp_reply.nb_received == 1) {
495cdb068f0SJianfeng Tan 			mp_rep = &mp_reply.msgs[0];
496cdb068f0SJianfeng Tan 			resp = (struct vdev_param *)mp_rep->param;
497cdb068f0SJianfeng Tan 			VDEV_LOG(INFO, "Received %d vdevs", resp->num);
49866fd3a3bSPaul Luse 			free(mp_reply.msgs);
499cdb068f0SJianfeng Tan 		} else
500cdb068f0SJianfeng Tan 			VDEV_LOG(ERR, "Failed to request vdev from primary");
501cdb068f0SJianfeng Tan 
502cdb068f0SJianfeng Tan 		/* Fall through to allow private vdevs in secondary process */
503cdb068f0SJianfeng Tan 	}
504cdb068f0SJianfeng Tan 
505edf73dd3SAnatoly Burakov scan:
5063b792ed9SThomas Monjalon 	/* call custom scan callbacks if any */
5073b792ed9SThomas Monjalon 	rte_spinlock_lock(&vdev_custom_scan_lock);
5083b792ed9SThomas Monjalon 	TAILQ_FOREACH(custom_scan, &vdev_custom_scans, next) {
5093b792ed9SThomas Monjalon 		if (custom_scan->callback != NULL)
5103b792ed9SThomas Monjalon 			/*
5113b792ed9SThomas Monjalon 			 * the callback should update devargs list
5128e6c3b79SGaetan Rivet 			 * by calling rte_devargs_insert() with
5133b792ed9SThomas Monjalon 			 *     devargs.bus = rte_bus_find_by_name("vdev");
5143b792ed9SThomas Monjalon 			 *     devargs.type = RTE_DEVTYPE_VIRTUAL;
515c753160dSDavid Marchand 			 *     devargs.policy = RTE_DEV_ALLOWED;
5163b792ed9SThomas Monjalon 			 */
5173b792ed9SThomas Monjalon 			custom_scan->callback(custom_scan->user_arg);
5183b792ed9SThomas Monjalon 	}
5193b792ed9SThomas Monjalon 	rte_spinlock_unlock(&vdev_custom_scan_lock);
520d4a586d2SJianfeng Tan 
521d4a586d2SJianfeng Tan 	/* for virtual devices we scan the devargs_list populated via cmdline */
5221f6d16eeSGaetan Rivet 	RTE_EAL_DEVARGS_FOREACH("vdev", devargs) {
523d4a586d2SJianfeng Tan 
524d4a586d2SJianfeng Tan 		dev = calloc(1, sizeof(*dev));
525d4a586d2SJianfeng Tan 		if (!dev)
526d4a586d2SJianfeng Tan 			return -1;
527d4a586d2SJianfeng Tan 
528f14b264fSThomas Monjalon 		rte_spinlock_recursive_lock(&vdev_device_list_lock);
52935f46283SJianfeng Tan 
53035f46283SJianfeng Tan 		if (find_vdev(devargs->name)) {
531f14b264fSThomas Monjalon 			rte_spinlock_recursive_unlock(&vdev_device_list_lock);
53235f46283SJianfeng Tan 			free(dev);
53335f46283SJianfeng Tan 			continue;
53435f46283SJianfeng Tan 		}
53535f46283SJianfeng Tan 
5366844d146SThomas Monjalon 		dev->device.bus = &rte_vdev_bus;
537d4a586d2SJianfeng Tan 		dev->device.devargs = devargs;
538d4a586d2SJianfeng Tan 		dev->device.numa_node = SOCKET_ID_ANY;
539d4a586d2SJianfeng Tan 		dev->device.name = devargs->name;
540d4a586d2SJianfeng Tan 
541d4a586d2SJianfeng Tan 		TAILQ_INSERT_TAIL(&vdev_device_list, dev, next);
54235f46283SJianfeng Tan 
543f14b264fSThomas Monjalon 		rte_spinlock_recursive_unlock(&vdev_device_list_lock);
544d4a586d2SJianfeng Tan 	}
545d4a586d2SJianfeng Tan 
546d4a586d2SJianfeng Tan 	return 0;
547d4a586d2SJianfeng Tan }
548d4a586d2SJianfeng Tan 
549d4a586d2SJianfeng Tan static int
550d4a586d2SJianfeng Tan vdev_probe(void)
551d4a586d2SJianfeng Tan {
552d4a586d2SJianfeng Tan 	struct rte_vdev_device *dev;
553e892fa59SRaslan Darawsheh 	int r, ret = 0;
554d4a586d2SJianfeng Tan 
555d4a586d2SJianfeng Tan 	/* call the init function for each virtual device */
556d4a586d2SJianfeng Tan 	TAILQ_FOREACH(dev, &vdev_device_list, next) {
55735f46283SJianfeng Tan 		/* we don't use the vdev lock here, as it's only used in DPDK
55835f46283SJianfeng Tan 		 * initialization; and we don't want to hold such a lock when
55935f46283SJianfeng Tan 		 * we call each driver probe.
56035f46283SJianfeng Tan 		 */
561d4a586d2SJianfeng Tan 
562e892fa59SRaslan Darawsheh 		r = vdev_probe_all_drivers(dev);
563e892fa59SRaslan Darawsheh 		if (r != 0) {
564e892fa59SRaslan Darawsheh 			if (r == -EEXIST)
565d4a586d2SJianfeng Tan 				continue;
566999951c8SStephen Hemminger 			VDEV_LOG(ERR, "failed to initialize %s device",
567d4a586d2SJianfeng Tan 				rte_vdev_device_name(dev));
5689576ded3SMoti Haimovsky 			ret = -1;
569d4a586d2SJianfeng Tan 		}
570d4a586d2SJianfeng Tan 	}
571d4a586d2SJianfeng Tan 
5729576ded3SMoti Haimovsky 	return ret;
573d4a586d2SJianfeng Tan }
574d4a586d2SJianfeng Tan 
5751cab1a40SKevin Laatz static int
5761cab1a40SKevin Laatz vdev_cleanup(void)
5771cab1a40SKevin Laatz {
5781cab1a40SKevin Laatz 	struct rte_vdev_device *dev, *tmp_dev;
5791cab1a40SKevin Laatz 	int error = 0;
5801cab1a40SKevin Laatz 
5811cab1a40SKevin Laatz 	RTE_TAILQ_FOREACH_SAFE(dev, &vdev_device_list, next, tmp_dev) {
5821cab1a40SKevin Laatz 		const struct rte_vdev_driver *drv;
5831cab1a40SKevin Laatz 		int ret = 0;
5841cab1a40SKevin Laatz 
5853f27defeSZhangfei Gao 		if (dev->device.driver == NULL)
586deb44af7SVolodymyr Fialko 			goto free;
5873f27defeSZhangfei Gao 
5881cab1a40SKevin Laatz 		drv = container_of(dev->device.driver, const struct rte_vdev_driver, driver);
5891cab1a40SKevin Laatz 
5903f27defeSZhangfei Gao 		if (drv->remove == NULL)
591deb44af7SVolodymyr Fialko 			goto free;
5921cab1a40SKevin Laatz 
5931cab1a40SKevin Laatz 		ret = drv->remove(dev);
5941cab1a40SKevin Laatz 		if (ret < 0)
5951cab1a40SKevin Laatz 			error = -1;
5961cab1a40SKevin Laatz 
5971cab1a40SKevin Laatz 		dev->device.driver = NULL;
598deb44af7SVolodymyr Fialko free:
5991cab1a40SKevin Laatz 		free(dev);
6001cab1a40SKevin Laatz 	}
6011cab1a40SKevin Laatz 
6021cab1a40SKevin Laatz 	return error;
6031cab1a40SKevin Laatz }
6041cab1a40SKevin Laatz 
605ac91bc49SGaetan Rivet struct rte_device *
606ac91bc49SGaetan Rivet rte_vdev_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,
607d4a586d2SJianfeng Tan 		     const void *data)
608d4a586d2SJianfeng Tan {
6093701b792SGaetan Rivet 	const struct rte_vdev_device *vstart;
610d4a586d2SJianfeng Tan 	struct rte_vdev_device *dev;
611d4a586d2SJianfeng Tan 
612f14b264fSThomas Monjalon 	rte_spinlock_recursive_lock(&vdev_device_list_lock);
6133701b792SGaetan Rivet 	if (start != NULL) {
6143701b792SGaetan Rivet 		vstart = RTE_DEV_TO_VDEV_CONST(start);
6153701b792SGaetan Rivet 		dev = TAILQ_NEXT(vstart, next);
6163701b792SGaetan Rivet 	} else {
6173701b792SGaetan Rivet 		dev = TAILQ_FIRST(&vdev_device_list);
618d4a586d2SJianfeng Tan 	}
6193701b792SGaetan Rivet 	while (dev != NULL) {
620d4a586d2SJianfeng Tan 		if (cmp(&dev->device, data) == 0)
62135f46283SJianfeng Tan 			break;
6223701b792SGaetan Rivet 		dev = TAILQ_NEXT(dev, next);
623d4a586d2SJianfeng Tan 	}
624f14b264fSThomas Monjalon 	rte_spinlock_recursive_unlock(&vdev_device_list_lock);
62535f46283SJianfeng Tan 
62635f46283SJianfeng Tan 	return dev ? &dev->device : NULL;
627d4a586d2SJianfeng Tan }
628d4a586d2SJianfeng Tan 
629d4a586d2SJianfeng Tan static int
630d4a586d2SJianfeng Tan vdev_plug(struct rte_device *dev)
631d4a586d2SJianfeng Tan {
632d4a586d2SJianfeng Tan 	return vdev_probe_all_drivers(RTE_DEV_TO_VDEV(dev));
633d4a586d2SJianfeng Tan }
634d4a586d2SJianfeng Tan 
635d4a586d2SJianfeng Tan static int
636d4a586d2SJianfeng Tan vdev_unplug(struct rte_device *dev)
637d4a586d2SJianfeng Tan {
638d4a586d2SJianfeng Tan 	return rte_vdev_uninit(dev->name);
639d4a586d2SJianfeng Tan }
640d4a586d2SJianfeng Tan 
6418d935fffSMaxime Coquelin static enum rte_iova_mode
6428d935fffSMaxime Coquelin vdev_get_iommu_class(void)
6438d935fffSMaxime Coquelin {
6448d935fffSMaxime Coquelin 	const char *name;
6458d935fffSMaxime Coquelin 	struct rte_vdev_device *dev;
6468d935fffSMaxime Coquelin 	struct rte_vdev_driver *driver;
6478d935fffSMaxime Coquelin 
6488d935fffSMaxime Coquelin 	TAILQ_FOREACH(dev, &vdev_device_list, next) {
6498d935fffSMaxime Coquelin 		name = rte_vdev_device_name(dev);
6508d935fffSMaxime Coquelin 		if (vdev_parse(name, &driver))
6518d935fffSMaxime Coquelin 			continue;
6528d935fffSMaxime Coquelin 
6538d935fffSMaxime Coquelin 		if (driver->drv_flags & RTE_VDEV_DRV_NEED_IOVA_AS_VA)
6548d935fffSMaxime Coquelin 			return RTE_IOVA_VA;
6558d935fffSMaxime Coquelin 	}
6568d935fffSMaxime Coquelin 
6578d935fffSMaxime Coquelin 	return RTE_IOVA_DC;
6588d935fffSMaxime Coquelin }
6598d935fffSMaxime Coquelin 
660d4a586d2SJianfeng Tan static struct rte_bus rte_vdev_bus = {
661d4a586d2SJianfeng Tan 	.scan = vdev_scan,
662d4a586d2SJianfeng Tan 	.probe = vdev_probe,
6631cab1a40SKevin Laatz 	.cleanup = vdev_cleanup,
664ac91bc49SGaetan Rivet 	.find_device = rte_vdev_find_device,
665d4a586d2SJianfeng Tan 	.plug = vdev_plug,
666d4a586d2SJianfeng Tan 	.unplug = vdev_unplug,
667d4a586d2SJianfeng Tan 	.parse = vdev_parse,
6686a2288edSMaxime Coquelin 	.dma_map = vdev_dma_map,
6696a2288edSMaxime Coquelin 	.dma_unmap = vdev_dma_unmap,
6708d935fffSMaxime Coquelin 	.get_iommu_class = vdev_get_iommu_class,
671ac91bc49SGaetan Rivet 	.dev_iterate = rte_vdev_dev_iterate,
672d4a586d2SJianfeng Tan };
673d4a586d2SJianfeng Tan 
674d4a586d2SJianfeng Tan RTE_REGISTER_BUS(vdev, rte_vdev_bus);
675eeded204SDavid Marchand RTE_LOG_REGISTER_DEFAULT(vdev_logtype_bus, NOTICE);
676