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>
14d4a586d2SJianfeng Tan #include <rte_dev.h>
15d4a586d2SJianfeng Tan #include <rte_bus.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
24d4a586d2SJianfeng Tan #include "rte_bus_vdev.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 /** Double linked list of virtual device drivers. */
34d4a586d2SJianfeng Tan TAILQ_HEAD(vdev_device_list, rte_vdev_device);
35d4a586d2SJianfeng Tan
36d4a586d2SJianfeng Tan static struct vdev_device_list vdev_device_list =
37d4a586d2SJianfeng Tan TAILQ_HEAD_INITIALIZER(vdev_device_list);
38f14b264fSThomas Monjalon /* The lock needs to be recursive because a vdev can manage another vdev. */
39f14b264fSThomas Monjalon static rte_spinlock_recursive_t vdev_device_list_lock =
40f14b264fSThomas Monjalon RTE_SPINLOCK_RECURSIVE_INITIALIZER;
4135f46283SJianfeng Tan
42b74fd6b8SFerruh Yigit static struct vdev_driver_list vdev_driver_list =
43d4a586d2SJianfeng Tan TAILQ_HEAD_INITIALIZER(vdev_driver_list);
44d4a586d2SJianfeng Tan
453b792ed9SThomas Monjalon struct vdev_custom_scan {
463b792ed9SThomas Monjalon TAILQ_ENTRY(vdev_custom_scan) next;
473b792ed9SThomas Monjalon rte_vdev_scan_callback callback;
483b792ed9SThomas Monjalon void *user_arg;
493b792ed9SThomas Monjalon };
503b792ed9SThomas Monjalon TAILQ_HEAD(vdev_custom_scans, vdev_custom_scan);
513b792ed9SThomas Monjalon static struct vdev_custom_scans vdev_custom_scans =
523b792ed9SThomas Monjalon TAILQ_HEAD_INITIALIZER(vdev_custom_scans);
533b792ed9SThomas Monjalon static rte_spinlock_t vdev_custom_scan_lock = RTE_SPINLOCK_INITIALIZER;
543b792ed9SThomas Monjalon
55d4a586d2SJianfeng Tan /* register a driver */
56d4a586d2SJianfeng Tan void
rte_vdev_register(struct rte_vdev_driver * driver)57d4a586d2SJianfeng Tan rte_vdev_register(struct rte_vdev_driver *driver)
58d4a586d2SJianfeng Tan {
59d4a586d2SJianfeng Tan TAILQ_INSERT_TAIL(&vdev_driver_list, driver, next);
60d4a586d2SJianfeng Tan }
61d4a586d2SJianfeng Tan
62d4a586d2SJianfeng Tan /* unregister a driver */
63d4a586d2SJianfeng Tan void
rte_vdev_unregister(struct rte_vdev_driver * driver)64d4a586d2SJianfeng Tan rte_vdev_unregister(struct rte_vdev_driver *driver)
65d4a586d2SJianfeng Tan {
66d4a586d2SJianfeng Tan TAILQ_REMOVE(&vdev_driver_list, driver, next);
67d4a586d2SJianfeng Tan }
68d4a586d2SJianfeng Tan
693b792ed9SThomas Monjalon int
rte_vdev_add_custom_scan(rte_vdev_scan_callback callback,void * user_arg)703b792ed9SThomas Monjalon rte_vdev_add_custom_scan(rte_vdev_scan_callback callback, void *user_arg)
713b792ed9SThomas Monjalon {
723b792ed9SThomas Monjalon struct vdev_custom_scan *custom_scan;
733b792ed9SThomas Monjalon
743b792ed9SThomas Monjalon rte_spinlock_lock(&vdev_custom_scan_lock);
753b792ed9SThomas Monjalon
763b792ed9SThomas Monjalon /* check if already registered */
773b792ed9SThomas Monjalon TAILQ_FOREACH(custom_scan, &vdev_custom_scans, next) {
783b792ed9SThomas Monjalon if (custom_scan->callback == callback &&
793b792ed9SThomas Monjalon custom_scan->user_arg == user_arg)
803b792ed9SThomas Monjalon break;
813b792ed9SThomas Monjalon }
823b792ed9SThomas Monjalon
833b792ed9SThomas Monjalon if (custom_scan == NULL) {
843b792ed9SThomas Monjalon custom_scan = malloc(sizeof(struct vdev_custom_scan));
853b792ed9SThomas Monjalon if (custom_scan != NULL) {
863b792ed9SThomas Monjalon custom_scan->callback = callback;
873b792ed9SThomas Monjalon custom_scan->user_arg = user_arg;
883b792ed9SThomas Monjalon TAILQ_INSERT_TAIL(&vdev_custom_scans, custom_scan, next);
893b792ed9SThomas Monjalon }
903b792ed9SThomas Monjalon }
913b792ed9SThomas Monjalon
923b792ed9SThomas Monjalon rte_spinlock_unlock(&vdev_custom_scan_lock);
933b792ed9SThomas Monjalon
943b792ed9SThomas Monjalon return (custom_scan == NULL) ? -1 : 0;
953b792ed9SThomas Monjalon }
963b792ed9SThomas Monjalon
973b792ed9SThomas Monjalon int
rte_vdev_remove_custom_scan(rte_vdev_scan_callback callback,void * user_arg)983b792ed9SThomas Monjalon rte_vdev_remove_custom_scan(rte_vdev_scan_callback callback, void *user_arg)
993b792ed9SThomas Monjalon {
1003b792ed9SThomas Monjalon struct vdev_custom_scan *custom_scan, *tmp_scan;
1013b792ed9SThomas Monjalon
1023b792ed9SThomas Monjalon rte_spinlock_lock(&vdev_custom_scan_lock);
103*f1f6ebc0SWilliam Tu RTE_TAILQ_FOREACH_SAFE(custom_scan, &vdev_custom_scans, next,
104*f1f6ebc0SWilliam Tu tmp_scan) {
1053b792ed9SThomas Monjalon if (custom_scan->callback != callback ||
1063b792ed9SThomas Monjalon (custom_scan->user_arg != (void *)-1 &&
1073b792ed9SThomas Monjalon custom_scan->user_arg != user_arg))
1083b792ed9SThomas Monjalon continue;
1093b792ed9SThomas Monjalon TAILQ_REMOVE(&vdev_custom_scans, custom_scan, next);
1103b792ed9SThomas Monjalon free(custom_scan);
1113b792ed9SThomas Monjalon }
1123b792ed9SThomas Monjalon rte_spinlock_unlock(&vdev_custom_scan_lock);
1133b792ed9SThomas Monjalon
1143b792ed9SThomas Monjalon return 0;
1153b792ed9SThomas Monjalon }
1163b792ed9SThomas Monjalon
117d4a586d2SJianfeng Tan static int
vdev_parse(const char * name,void * addr)118d4a586d2SJianfeng Tan vdev_parse(const char *name, void *addr)
119d4a586d2SJianfeng Tan {
120d4a586d2SJianfeng Tan struct rte_vdev_driver **out = addr;
121d4a586d2SJianfeng Tan struct rte_vdev_driver *driver = NULL;
122d4a586d2SJianfeng Tan
123d4a586d2SJianfeng Tan TAILQ_FOREACH(driver, &vdev_driver_list, next) {
124d4a586d2SJianfeng Tan if (strncmp(driver->driver.name, name,
125d4a586d2SJianfeng Tan strlen(driver->driver.name)) == 0)
126d4a586d2SJianfeng Tan break;
127d4a586d2SJianfeng Tan if (driver->driver.alias &&
128d4a586d2SJianfeng Tan strncmp(driver->driver.alias, name,
129d4a586d2SJianfeng Tan strlen(driver->driver.alias)) == 0)
130d4a586d2SJianfeng Tan break;
131d4a586d2SJianfeng Tan }
132d4a586d2SJianfeng Tan if (driver != NULL &&
133d4a586d2SJianfeng Tan addr != NULL)
134d4a586d2SJianfeng Tan *out = driver;
135d4a586d2SJianfeng Tan return driver == NULL;
136d4a586d2SJianfeng Tan }
137d4a586d2SJianfeng Tan
138d4a586d2SJianfeng Tan static int
vdev_dma_map(struct rte_device * dev,void * addr,uint64_t iova,size_t len)1396a2288edSMaxime Coquelin vdev_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
1406a2288edSMaxime Coquelin {
1416a2288edSMaxime Coquelin struct rte_vdev_device *vdev = RTE_DEV_TO_VDEV(dev);
1426a2288edSMaxime Coquelin const struct rte_vdev_driver *driver;
1436a2288edSMaxime Coquelin
1446a2288edSMaxime Coquelin if (!vdev) {
1456a2288edSMaxime Coquelin rte_errno = EINVAL;
1466a2288edSMaxime Coquelin return -1;
1476a2288edSMaxime Coquelin }
1486a2288edSMaxime Coquelin
1496a2288edSMaxime Coquelin if (!vdev->device.driver) {
1506a2288edSMaxime Coquelin VDEV_LOG(DEBUG, "no driver attach to device %s", dev->name);
1516a2288edSMaxime Coquelin return 1;
1526a2288edSMaxime Coquelin }
1536a2288edSMaxime Coquelin
1546a2288edSMaxime Coquelin driver = container_of(vdev->device.driver, const struct rte_vdev_driver,
1556a2288edSMaxime Coquelin driver);
1566a2288edSMaxime Coquelin
1576a2288edSMaxime Coquelin if (driver->dma_map)
1586a2288edSMaxime Coquelin return driver->dma_map(vdev, addr, iova, len);
1596a2288edSMaxime Coquelin
1606a2288edSMaxime Coquelin return 0;
1616a2288edSMaxime Coquelin }
1626a2288edSMaxime Coquelin
1636a2288edSMaxime Coquelin static int
vdev_dma_unmap(struct rte_device * dev,void * addr,uint64_t iova,size_t len)1646a2288edSMaxime Coquelin vdev_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
1656a2288edSMaxime Coquelin {
1666a2288edSMaxime Coquelin struct rte_vdev_device *vdev = RTE_DEV_TO_VDEV(dev);
1676a2288edSMaxime Coquelin const struct rte_vdev_driver *driver;
1686a2288edSMaxime Coquelin
1696a2288edSMaxime Coquelin if (!vdev) {
1706a2288edSMaxime Coquelin rte_errno = EINVAL;
1716a2288edSMaxime Coquelin return -1;
1726a2288edSMaxime Coquelin }
1736a2288edSMaxime Coquelin
1746a2288edSMaxime Coquelin if (!vdev->device.driver) {
1756a2288edSMaxime Coquelin VDEV_LOG(DEBUG, "no driver attach to device %s", dev->name);
1766a2288edSMaxime Coquelin return 1;
1776a2288edSMaxime Coquelin }
1786a2288edSMaxime Coquelin
1796a2288edSMaxime Coquelin driver = container_of(vdev->device.driver, const struct rte_vdev_driver,
1806a2288edSMaxime Coquelin driver);
1816a2288edSMaxime Coquelin
1826a2288edSMaxime Coquelin if (driver->dma_unmap)
1836a2288edSMaxime Coquelin return driver->dma_unmap(vdev, addr, iova, len);
1846a2288edSMaxime Coquelin
1856a2288edSMaxime Coquelin return 0;
1866a2288edSMaxime Coquelin }
1876a2288edSMaxime Coquelin
1886a2288edSMaxime Coquelin static int
vdev_probe_all_drivers(struct rte_vdev_device * dev)189d4a586d2SJianfeng Tan vdev_probe_all_drivers(struct rte_vdev_device *dev)
190d4a586d2SJianfeng Tan {
191d4a586d2SJianfeng Tan const char *name;
192d4a586d2SJianfeng Tan struct rte_vdev_driver *driver;
1938d935fffSMaxime Coquelin enum rte_iova_mode iova_mode;
194d4a586d2SJianfeng Tan int ret;
195d4a586d2SJianfeng Tan
196e892fa59SRaslan Darawsheh if (rte_dev_is_probed(&dev->device))
197e892fa59SRaslan Darawsheh return -EEXIST;
198e892fa59SRaslan Darawsheh
199d4a586d2SJianfeng Tan name = rte_vdev_device_name(dev);
2004169ed6eSThomas Monjalon VDEV_LOG(DEBUG, "Search driver to probe device %s", name);
201d4a586d2SJianfeng Tan
202d4a586d2SJianfeng Tan if (vdev_parse(name, &driver))
203d4a586d2SJianfeng Tan return -1;
2048d935fffSMaxime Coquelin
2058d935fffSMaxime Coquelin iova_mode = rte_eal_iova_mode();
2068d935fffSMaxime Coquelin if ((driver->drv_flags & RTE_VDEV_DRV_NEED_IOVA_AS_VA) && (iova_mode == RTE_IOVA_PA)) {
2078d935fffSMaxime Coquelin VDEV_LOG(ERR, "%s requires VA IOVA mode but current mode is PA, not initializing",
2088d935fffSMaxime Coquelin name);
2098d935fffSMaxime Coquelin return -1;
2108d935fffSMaxime Coquelin }
2118d935fffSMaxime Coquelin
212d4a586d2SJianfeng Tan ret = driver->probe(dev);
213391797f0SThomas Monjalon if (ret == 0)
214391797f0SThomas Monjalon dev->device.driver = &driver->driver;
215d4a586d2SJianfeng Tan return ret;
216d4a586d2SJianfeng Tan }
217d4a586d2SJianfeng Tan
21835f46283SJianfeng Tan /* The caller shall be responsible for thread-safe */
219d4a586d2SJianfeng Tan static struct rte_vdev_device *
find_vdev(const char * name)220d4a586d2SJianfeng Tan find_vdev(const char *name)
221d4a586d2SJianfeng Tan {
222d4a586d2SJianfeng Tan struct rte_vdev_device *dev;
223d4a586d2SJianfeng Tan
224d4a586d2SJianfeng Tan if (!name)
225d4a586d2SJianfeng Tan return NULL;
226d4a586d2SJianfeng Tan
227d4a586d2SJianfeng Tan TAILQ_FOREACH(dev, &vdev_device_list, next) {
228d4a586d2SJianfeng Tan const char *devname = rte_vdev_device_name(dev);
229d4a586d2SJianfeng Tan
230fada6963SNachiketa Prachanda if (!strcmp(devname, name))
231d4a586d2SJianfeng Tan return dev;
232d4a586d2SJianfeng Tan }
233d4a586d2SJianfeng Tan
234d4a586d2SJianfeng Tan return NULL;
235d4a586d2SJianfeng Tan }
236d4a586d2SJianfeng Tan
237d4a586d2SJianfeng Tan static struct rte_devargs *
alloc_devargs(const char * name,const char * args)238d4a586d2SJianfeng Tan alloc_devargs(const char *name, const char *args)
239d4a586d2SJianfeng Tan {
240d4a586d2SJianfeng Tan struct rte_devargs *devargs;
241d4a586d2SJianfeng Tan int ret;
242d4a586d2SJianfeng Tan
243d4a586d2SJianfeng Tan devargs = calloc(1, sizeof(*devargs));
244d4a586d2SJianfeng Tan if (!devargs)
245d4a586d2SJianfeng Tan return NULL;
246d4a586d2SJianfeng Tan
247d4a586d2SJianfeng Tan devargs->bus = &rte_vdev_bus;
248d4a586d2SJianfeng Tan if (args)
24964051bb1SXueming Li devargs->data = strdup(args);
250d4a586d2SJianfeng Tan else
25164051bb1SXueming Li devargs->data = strdup("");
25264051bb1SXueming Li devargs->args = devargs->data;
253d4a586d2SJianfeng Tan
254f9acaf84SBruce Richardson ret = strlcpy(devargs->name, name, sizeof(devargs->name));
255d4a586d2SJianfeng Tan if (ret < 0 || ret >= (int)sizeof(devargs->name)) {
25664051bb1SXueming Li rte_devargs_reset(devargs);
257d4a586d2SJianfeng Tan free(devargs);
258d4a586d2SJianfeng Tan return NULL;
259d4a586d2SJianfeng Tan }
260d4a586d2SJianfeng Tan
261d4a586d2SJianfeng Tan return devargs;
262d4a586d2SJianfeng Tan }
263d4a586d2SJianfeng Tan
26435f46283SJianfeng Tan static int
insert_vdev(const char * name,const char * args,struct rte_vdev_device ** p_dev,bool init)265f5b2eff0SQi Zhang insert_vdev(const char *name, const char *args,
266f5b2eff0SQi Zhang struct rte_vdev_device **p_dev,
267f5b2eff0SQi Zhang bool init)
268d4a586d2SJianfeng Tan {
269d4a586d2SJianfeng Tan struct rte_vdev_device *dev;
270d4a586d2SJianfeng Tan struct rte_devargs *devargs;
271d4a586d2SJianfeng Tan int ret;
272d4a586d2SJianfeng Tan
273d4a586d2SJianfeng Tan if (name == NULL)
274d4a586d2SJianfeng Tan return -EINVAL;
275d4a586d2SJianfeng Tan
276d4a586d2SJianfeng Tan devargs = alloc_devargs(name, args);
277d4a586d2SJianfeng Tan if (!devargs)
278d4a586d2SJianfeng Tan return -ENOMEM;
279d4a586d2SJianfeng Tan
280d4a586d2SJianfeng Tan dev = calloc(1, sizeof(*dev));
281d4a586d2SJianfeng Tan if (!dev) {
282d4a586d2SJianfeng Tan ret = -ENOMEM;
283d4a586d2SJianfeng Tan goto fail;
284d4a586d2SJianfeng Tan }
285d4a586d2SJianfeng Tan
2869ffe2f4eSQi Zhang dev->device.bus = &rte_vdev_bus;
287d4a586d2SJianfeng Tan dev->device.numa_node = SOCKET_ID_ANY;
288d4a586d2SJianfeng Tan dev->device.name = devargs->name;
289d4a586d2SJianfeng Tan
29035f46283SJianfeng Tan if (find_vdev(name)) {
291e9d159c3SThomas Monjalon /*
292e9d159c3SThomas Monjalon * A vdev is expected to have only one port.
293e9d159c3SThomas Monjalon * So there is no reason to try probing again,
294e9d159c3SThomas Monjalon * even with new arguments.
295e9d159c3SThomas Monjalon */
29635f46283SJianfeng Tan ret = -EEXIST;
297d4a586d2SJianfeng Tan goto fail;
298d4a586d2SJianfeng Tan }
299d4a586d2SJianfeng Tan
300f5b2eff0SQi Zhang if (init)
301c7ad7754SThomas Monjalon rte_devargs_insert(&devargs);
302c7ad7754SThomas Monjalon dev->device.devargs = devargs;
303c7ad7754SThomas Monjalon TAILQ_INSERT_TAIL(&vdev_device_list, dev, next);
304d4a586d2SJianfeng Tan
30535f46283SJianfeng Tan if (p_dev)
30635f46283SJianfeng Tan *p_dev = dev;
307d4a586d2SJianfeng Tan
30835f46283SJianfeng Tan return 0;
309d4a586d2SJianfeng Tan fail:
31064051bb1SXueming Li rte_devargs_reset(devargs);
311d4a586d2SJianfeng Tan free(devargs);
312d4a586d2SJianfeng Tan free(dev);
313d4a586d2SJianfeng Tan return ret;
314d4a586d2SJianfeng Tan }
315d4a586d2SJianfeng Tan
31635f46283SJianfeng Tan int
rte_vdev_init(const char * name,const char * args)31735f46283SJianfeng Tan rte_vdev_init(const char *name, const char *args)
31835f46283SJianfeng Tan {
31935f46283SJianfeng Tan struct rte_vdev_device *dev;
32035f46283SJianfeng Tan int ret;
32135f46283SJianfeng Tan
322f14b264fSThomas Monjalon rte_spinlock_recursive_lock(&vdev_device_list_lock);
323f5b2eff0SQi Zhang ret = insert_vdev(name, args, &dev, true);
32435f46283SJianfeng Tan if (ret == 0) {
32535f46283SJianfeng Tan ret = vdev_probe_all_drivers(dev);
32635f46283SJianfeng Tan if (ret) {
32735f46283SJianfeng Tan if (ret > 0)
328999951c8SStephen Hemminger VDEV_LOG(ERR, "no driver found for %s", name);
32935f46283SJianfeng Tan /* If fails, remove it from vdev list */
33035f46283SJianfeng Tan TAILQ_REMOVE(&vdev_device_list, dev, next);
3312effa126SThomas Monjalon rte_devargs_remove(dev->device.devargs);
33235f46283SJianfeng Tan free(dev);
33335f46283SJianfeng Tan }
33435f46283SJianfeng Tan }
335f14b264fSThomas Monjalon rte_spinlock_recursive_unlock(&vdev_device_list_lock);
33635f46283SJianfeng Tan return ret;
33735f46283SJianfeng Tan }
33835f46283SJianfeng Tan
339d4a586d2SJianfeng Tan static int
vdev_remove_driver(struct rte_vdev_device * dev)340d4a586d2SJianfeng Tan vdev_remove_driver(struct rte_vdev_device *dev)
341d4a586d2SJianfeng Tan {
342d4a586d2SJianfeng Tan const char *name = rte_vdev_device_name(dev);
343d4a586d2SJianfeng Tan const struct rte_vdev_driver *driver;
344d4a586d2SJianfeng Tan
345d4a586d2SJianfeng Tan if (!dev->device.driver) {
346999951c8SStephen Hemminger VDEV_LOG(DEBUG, "no driver attach to device %s", name);
347d4a586d2SJianfeng Tan return 1;
348d4a586d2SJianfeng Tan }
349d4a586d2SJianfeng Tan
350d4a586d2SJianfeng Tan driver = container_of(dev->device.driver, const struct rte_vdev_driver,
351d4a586d2SJianfeng Tan driver);
352d4a586d2SJianfeng Tan return driver->remove(dev);
353d4a586d2SJianfeng Tan }
354d4a586d2SJianfeng Tan
355d4a586d2SJianfeng Tan int
rte_vdev_uninit(const char * name)356d4a586d2SJianfeng Tan rte_vdev_uninit(const char *name)
357d4a586d2SJianfeng Tan {
358d4a586d2SJianfeng Tan struct rte_vdev_device *dev;
359d4a586d2SJianfeng Tan int ret;
360d4a586d2SJianfeng Tan
361d4a586d2SJianfeng Tan if (name == NULL)
362d4a586d2SJianfeng Tan return -EINVAL;
363d4a586d2SJianfeng Tan
364f14b264fSThomas Monjalon rte_spinlock_recursive_lock(&vdev_device_list_lock);
365d4a586d2SJianfeng Tan
36635f46283SJianfeng Tan dev = find_vdev(name);
36735f46283SJianfeng Tan if (!dev) {
36835f46283SJianfeng Tan ret = -ENOENT;
36935f46283SJianfeng Tan goto unlock;
37035f46283SJianfeng Tan }
371d4a586d2SJianfeng Tan
372d4a586d2SJianfeng Tan ret = vdev_remove_driver(dev);
373d4a586d2SJianfeng Tan if (ret)
37435f46283SJianfeng Tan goto unlock;
375d4a586d2SJianfeng Tan
376d4a586d2SJianfeng Tan TAILQ_REMOVE(&vdev_device_list, dev, next);
3772effa126SThomas Monjalon rte_devargs_remove(dev->device.devargs);
378d4a586d2SJianfeng Tan free(dev);
37935f46283SJianfeng Tan
38035f46283SJianfeng Tan unlock:
381f14b264fSThomas Monjalon rte_spinlock_recursive_unlock(&vdev_device_list_lock);
38235f46283SJianfeng Tan return ret;
383d4a586d2SJianfeng Tan }
384d4a586d2SJianfeng Tan
385cdb068f0SJianfeng Tan struct vdev_param {
386cdb068f0SJianfeng Tan #define VDEV_SCAN_REQ 1
387cdb068f0SJianfeng Tan #define VDEV_SCAN_ONE 2
388cdb068f0SJianfeng Tan #define VDEV_SCAN_REP 3
389cdb068f0SJianfeng Tan int type;
390cdb068f0SJianfeng Tan int num;
391cdb068f0SJianfeng Tan char name[RTE_DEV_NAME_MAX_LEN];
392cdb068f0SJianfeng Tan };
393cdb068f0SJianfeng Tan
394cdb068f0SJianfeng Tan static int vdev_plug(struct rte_device *dev);
395cdb068f0SJianfeng Tan
396cdb068f0SJianfeng Tan /**
397cdb068f0SJianfeng Tan * This function works as the action for both primary and secondary process
398cdb068f0SJianfeng Tan * for static vdev discovery when a secondary process is booting.
399cdb068f0SJianfeng Tan *
400cdb068f0SJianfeng Tan * step 1, secondary process sends a sync request to ask for vdev in primary;
401cdb068f0SJianfeng Tan * step 2, primary process receives the request, and send vdevs one by one;
402cdb068f0SJianfeng Tan * step 3, primary process sends back reply, which indicates how many vdevs
403cdb068f0SJianfeng Tan * are sent.
404cdb068f0SJianfeng Tan */
405cdb068f0SJianfeng Tan static int
vdev_action(const struct rte_mp_msg * mp_msg,const void * peer)406cdb068f0SJianfeng Tan vdev_action(const struct rte_mp_msg *mp_msg, const void *peer)
407cdb068f0SJianfeng Tan {
408cdb068f0SJianfeng Tan struct rte_vdev_device *dev;
409cdb068f0SJianfeng Tan struct rte_mp_msg mp_resp;
410cdb068f0SJianfeng Tan struct vdev_param *ou = (struct vdev_param *)&mp_resp.param;
411cdb068f0SJianfeng Tan const struct vdev_param *in = (const struct vdev_param *)mp_msg->param;
412cdb068f0SJianfeng Tan const char *devname;
413cdb068f0SJianfeng Tan int num;
41423f1c424SQi Zhang int ret;
415cdb068f0SJianfeng Tan
416cdb068f0SJianfeng Tan strlcpy(mp_resp.name, VDEV_MP_KEY, sizeof(mp_resp.name));
417cdb068f0SJianfeng Tan mp_resp.len_param = sizeof(*ou);
418cdb068f0SJianfeng Tan mp_resp.num_fds = 0;
419cdb068f0SJianfeng Tan
420cdb068f0SJianfeng Tan switch (in->type) {
421cdb068f0SJianfeng Tan case VDEV_SCAN_REQ:
422cdb068f0SJianfeng Tan ou->type = VDEV_SCAN_ONE;
423cdb068f0SJianfeng Tan ou->num = 1;
424cdb068f0SJianfeng Tan num = 0;
425cdb068f0SJianfeng Tan
426f14b264fSThomas Monjalon rte_spinlock_recursive_lock(&vdev_device_list_lock);
427cdb068f0SJianfeng Tan TAILQ_FOREACH(dev, &vdev_device_list, next) {
428cdb068f0SJianfeng Tan devname = rte_vdev_device_name(dev);
429cdb068f0SJianfeng Tan if (strlen(devname) == 0) {
430cdb068f0SJianfeng Tan VDEV_LOG(INFO, "vdev with no name is not sent");
431cdb068f0SJianfeng Tan continue;
432cdb068f0SJianfeng Tan }
433cdb068f0SJianfeng Tan VDEV_LOG(INFO, "send vdev, %s", devname);
434cdb068f0SJianfeng Tan strlcpy(ou->name, devname, RTE_DEV_NAME_MAX_LEN);
435cdb068f0SJianfeng Tan if (rte_mp_sendmsg(&mp_resp) < 0)
436cdb068f0SJianfeng Tan VDEV_LOG(ERR, "send vdev, %s, failed, %s",
437cdb068f0SJianfeng Tan devname, strerror(rte_errno));
438cdb068f0SJianfeng Tan num++;
439cdb068f0SJianfeng Tan }
440f14b264fSThomas Monjalon rte_spinlock_recursive_unlock(&vdev_device_list_lock);
441cdb068f0SJianfeng Tan
442cdb068f0SJianfeng Tan ou->type = VDEV_SCAN_REP;
443cdb068f0SJianfeng Tan ou->num = num;
444cdb068f0SJianfeng Tan if (rte_mp_reply(&mp_resp, peer) < 0)
445cdb068f0SJianfeng Tan VDEV_LOG(ERR, "Failed to reply a scan request");
446cdb068f0SJianfeng Tan break;
447cdb068f0SJianfeng Tan case VDEV_SCAN_ONE:
448cdb068f0SJianfeng Tan VDEV_LOG(INFO, "receive vdev, %s", in->name);
449f5b2eff0SQi Zhang ret = insert_vdev(in->name, NULL, NULL, false);
45023f1c424SQi Zhang if (ret == -EEXIST)
45123f1c424SQi Zhang VDEV_LOG(DEBUG, "device already exist, %s", in->name);
45223f1c424SQi Zhang else if (ret < 0)
453cdb068f0SJianfeng Tan VDEV_LOG(ERR, "failed to add vdev, %s", in->name);
454cdb068f0SJianfeng Tan break;
455cdb068f0SJianfeng Tan default:
456cdb068f0SJianfeng Tan VDEV_LOG(ERR, "vdev cannot recognize this message");
457cdb068f0SJianfeng Tan }
458cdb068f0SJianfeng Tan
459cdb068f0SJianfeng Tan return 0;
460cdb068f0SJianfeng Tan }
461cdb068f0SJianfeng Tan
462d4a586d2SJianfeng Tan static int
vdev_scan(void)463d4a586d2SJianfeng Tan vdev_scan(void)
464d4a586d2SJianfeng Tan {
465d4a586d2SJianfeng Tan struct rte_vdev_device *dev;
466d4a586d2SJianfeng Tan struct rte_devargs *devargs;
4673b792ed9SThomas Monjalon struct vdev_custom_scan *custom_scan;
4683b792ed9SThomas Monjalon
469cdb068f0SJianfeng Tan if (rte_mp_action_register(VDEV_MP_KEY, vdev_action) < 0 &&
470cdb068f0SJianfeng Tan rte_errno != EEXIST) {
471edf73dd3SAnatoly Burakov /* for primary, unsupported IPC is not an error */
472edf73dd3SAnatoly Burakov if (rte_eal_process_type() == RTE_PROC_PRIMARY &&
473edf73dd3SAnatoly Burakov rte_errno == ENOTSUP)
474edf73dd3SAnatoly Burakov goto scan;
475cdb068f0SJianfeng Tan VDEV_LOG(ERR, "Failed to add vdev mp action");
476cdb068f0SJianfeng Tan return -1;
477cdb068f0SJianfeng Tan }
478cdb068f0SJianfeng Tan
479cdb068f0SJianfeng Tan if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
480cdb068f0SJianfeng Tan struct rte_mp_msg mp_req, *mp_rep;
481cdb068f0SJianfeng Tan struct rte_mp_reply mp_reply;
482cdb068f0SJianfeng Tan struct timespec ts = {.tv_sec = 5, .tv_nsec = 0};
483cdb068f0SJianfeng Tan struct vdev_param *req = (struct vdev_param *)mp_req.param;
484cdb068f0SJianfeng Tan struct vdev_param *resp;
485cdb068f0SJianfeng Tan
486cdb068f0SJianfeng Tan strlcpy(mp_req.name, VDEV_MP_KEY, sizeof(mp_req.name));
487cdb068f0SJianfeng Tan mp_req.len_param = sizeof(*req);
488cdb068f0SJianfeng Tan mp_req.num_fds = 0;
489cdb068f0SJianfeng Tan req->type = VDEV_SCAN_REQ;
490cdb068f0SJianfeng Tan if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) == 0 &&
491cdb068f0SJianfeng Tan mp_reply.nb_received == 1) {
492cdb068f0SJianfeng Tan mp_rep = &mp_reply.msgs[0];
493cdb068f0SJianfeng Tan resp = (struct vdev_param *)mp_rep->param;
494cdb068f0SJianfeng Tan VDEV_LOG(INFO, "Received %d vdevs", resp->num);
49566fd3a3bSPaul Luse free(mp_reply.msgs);
496cdb068f0SJianfeng Tan } else
497cdb068f0SJianfeng Tan VDEV_LOG(ERR, "Failed to request vdev from primary");
498cdb068f0SJianfeng Tan
499cdb068f0SJianfeng Tan /* Fall through to allow private vdevs in secondary process */
500cdb068f0SJianfeng Tan }
501cdb068f0SJianfeng Tan
502edf73dd3SAnatoly Burakov scan:
5033b792ed9SThomas Monjalon /* call custom scan callbacks if any */
5043b792ed9SThomas Monjalon rte_spinlock_lock(&vdev_custom_scan_lock);
5053b792ed9SThomas Monjalon TAILQ_FOREACH(custom_scan, &vdev_custom_scans, next) {
5063b792ed9SThomas Monjalon if (custom_scan->callback != NULL)
5073b792ed9SThomas Monjalon /*
5083b792ed9SThomas Monjalon * the callback should update devargs list
5098e6c3b79SGaetan Rivet * by calling rte_devargs_insert() with
5103b792ed9SThomas Monjalon * devargs.bus = rte_bus_find_by_name("vdev");
5113b792ed9SThomas Monjalon * devargs.type = RTE_DEVTYPE_VIRTUAL;
512c753160dSDavid Marchand * devargs.policy = RTE_DEV_ALLOWED;
5133b792ed9SThomas Monjalon */
5143b792ed9SThomas Monjalon custom_scan->callback(custom_scan->user_arg);
5153b792ed9SThomas Monjalon }
5163b792ed9SThomas Monjalon rte_spinlock_unlock(&vdev_custom_scan_lock);
517d4a586d2SJianfeng Tan
518d4a586d2SJianfeng Tan /* for virtual devices we scan the devargs_list populated via cmdline */
5191f6d16eeSGaetan Rivet RTE_EAL_DEVARGS_FOREACH("vdev", devargs) {
520d4a586d2SJianfeng Tan
521d4a586d2SJianfeng Tan dev = calloc(1, sizeof(*dev));
522d4a586d2SJianfeng Tan if (!dev)
523d4a586d2SJianfeng Tan return -1;
524d4a586d2SJianfeng Tan
525f14b264fSThomas Monjalon rte_spinlock_recursive_lock(&vdev_device_list_lock);
52635f46283SJianfeng Tan
52735f46283SJianfeng Tan if (find_vdev(devargs->name)) {
528f14b264fSThomas Monjalon rte_spinlock_recursive_unlock(&vdev_device_list_lock);
52935f46283SJianfeng Tan free(dev);
53035f46283SJianfeng Tan continue;
53135f46283SJianfeng Tan }
53235f46283SJianfeng Tan
5336844d146SThomas Monjalon dev->device.bus = &rte_vdev_bus;
534d4a586d2SJianfeng Tan dev->device.devargs = devargs;
535d4a586d2SJianfeng Tan dev->device.numa_node = SOCKET_ID_ANY;
536d4a586d2SJianfeng Tan dev->device.name = devargs->name;
537d4a586d2SJianfeng Tan
538d4a586d2SJianfeng Tan TAILQ_INSERT_TAIL(&vdev_device_list, dev, next);
53935f46283SJianfeng Tan
540f14b264fSThomas Monjalon rte_spinlock_recursive_unlock(&vdev_device_list_lock);
541d4a586d2SJianfeng Tan }
542d4a586d2SJianfeng Tan
543d4a586d2SJianfeng Tan return 0;
544d4a586d2SJianfeng Tan }
545d4a586d2SJianfeng Tan
546d4a586d2SJianfeng Tan static int
vdev_probe(void)547d4a586d2SJianfeng Tan vdev_probe(void)
548d4a586d2SJianfeng Tan {
549d4a586d2SJianfeng Tan struct rte_vdev_device *dev;
550e892fa59SRaslan Darawsheh int r, ret = 0;
551d4a586d2SJianfeng Tan
552d4a586d2SJianfeng Tan /* call the init function for each virtual device */
553d4a586d2SJianfeng Tan TAILQ_FOREACH(dev, &vdev_device_list, next) {
55435f46283SJianfeng Tan /* we don't use the vdev lock here, as it's only used in DPDK
55535f46283SJianfeng Tan * initialization; and we don't want to hold such a lock when
55635f46283SJianfeng Tan * we call each driver probe.
55735f46283SJianfeng Tan */
558d4a586d2SJianfeng Tan
559e892fa59SRaslan Darawsheh r = vdev_probe_all_drivers(dev);
560e892fa59SRaslan Darawsheh if (r != 0) {
561e892fa59SRaslan Darawsheh if (r == -EEXIST)
562d4a586d2SJianfeng Tan continue;
563999951c8SStephen Hemminger VDEV_LOG(ERR, "failed to initialize %s device",
564d4a586d2SJianfeng Tan rte_vdev_device_name(dev));
5659576ded3SMoti Haimovsky ret = -1;
566d4a586d2SJianfeng Tan }
567d4a586d2SJianfeng Tan }
568d4a586d2SJianfeng Tan
5699576ded3SMoti Haimovsky return ret;
570d4a586d2SJianfeng Tan }
571d4a586d2SJianfeng Tan
572ac91bc49SGaetan Rivet struct rte_device *
rte_vdev_find_device(const struct rte_device * start,rte_dev_cmp_t cmp,const void * data)573ac91bc49SGaetan Rivet rte_vdev_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,
574d4a586d2SJianfeng Tan const void *data)
575d4a586d2SJianfeng Tan {
5763701b792SGaetan Rivet const struct rte_vdev_device *vstart;
577d4a586d2SJianfeng Tan struct rte_vdev_device *dev;
578d4a586d2SJianfeng Tan
579f14b264fSThomas Monjalon rte_spinlock_recursive_lock(&vdev_device_list_lock);
5803701b792SGaetan Rivet if (start != NULL) {
5813701b792SGaetan Rivet vstart = RTE_DEV_TO_VDEV_CONST(start);
5823701b792SGaetan Rivet dev = TAILQ_NEXT(vstart, next);
5833701b792SGaetan Rivet } else {
5843701b792SGaetan Rivet dev = TAILQ_FIRST(&vdev_device_list);
585d4a586d2SJianfeng Tan }
5863701b792SGaetan Rivet while (dev != NULL) {
587d4a586d2SJianfeng Tan if (cmp(&dev->device, data) == 0)
58835f46283SJianfeng Tan break;
5893701b792SGaetan Rivet dev = TAILQ_NEXT(dev, next);
590d4a586d2SJianfeng Tan }
591f14b264fSThomas Monjalon rte_spinlock_recursive_unlock(&vdev_device_list_lock);
59235f46283SJianfeng Tan
59335f46283SJianfeng Tan return dev ? &dev->device : NULL;
594d4a586d2SJianfeng Tan }
595d4a586d2SJianfeng Tan
596d4a586d2SJianfeng Tan static int
vdev_plug(struct rte_device * dev)597d4a586d2SJianfeng Tan vdev_plug(struct rte_device *dev)
598d4a586d2SJianfeng Tan {
599d4a586d2SJianfeng Tan return vdev_probe_all_drivers(RTE_DEV_TO_VDEV(dev));
600d4a586d2SJianfeng Tan }
601d4a586d2SJianfeng Tan
602d4a586d2SJianfeng Tan static int
vdev_unplug(struct rte_device * dev)603d4a586d2SJianfeng Tan vdev_unplug(struct rte_device *dev)
604d4a586d2SJianfeng Tan {
605d4a586d2SJianfeng Tan return rte_vdev_uninit(dev->name);
606d4a586d2SJianfeng Tan }
607d4a586d2SJianfeng Tan
6088d935fffSMaxime Coquelin static enum rte_iova_mode
vdev_get_iommu_class(void)6098d935fffSMaxime Coquelin vdev_get_iommu_class(void)
6108d935fffSMaxime Coquelin {
6118d935fffSMaxime Coquelin const char *name;
6128d935fffSMaxime Coquelin struct rte_vdev_device *dev;
6138d935fffSMaxime Coquelin struct rte_vdev_driver *driver;
6148d935fffSMaxime Coquelin
6158d935fffSMaxime Coquelin TAILQ_FOREACH(dev, &vdev_device_list, next) {
6168d935fffSMaxime Coquelin name = rte_vdev_device_name(dev);
6178d935fffSMaxime Coquelin if (vdev_parse(name, &driver))
6188d935fffSMaxime Coquelin continue;
6198d935fffSMaxime Coquelin
6208d935fffSMaxime Coquelin if (driver->drv_flags & RTE_VDEV_DRV_NEED_IOVA_AS_VA)
6218d935fffSMaxime Coquelin return RTE_IOVA_VA;
6228d935fffSMaxime Coquelin }
6238d935fffSMaxime Coquelin
6248d935fffSMaxime Coquelin return RTE_IOVA_DC;
6258d935fffSMaxime Coquelin }
6268d935fffSMaxime Coquelin
627d4a586d2SJianfeng Tan static struct rte_bus rte_vdev_bus = {
628d4a586d2SJianfeng Tan .scan = vdev_scan,
629d4a586d2SJianfeng Tan .probe = vdev_probe,
630ac91bc49SGaetan Rivet .find_device = rte_vdev_find_device,
631d4a586d2SJianfeng Tan .plug = vdev_plug,
632d4a586d2SJianfeng Tan .unplug = vdev_unplug,
633d4a586d2SJianfeng Tan .parse = vdev_parse,
6346a2288edSMaxime Coquelin .dma_map = vdev_dma_map,
6356a2288edSMaxime Coquelin .dma_unmap = vdev_dma_unmap,
6368d935fffSMaxime Coquelin .get_iommu_class = vdev_get_iommu_class,
637ac91bc49SGaetan Rivet .dev_iterate = rte_vdev_dev_iterate,
638d4a586d2SJianfeng Tan };
639d4a586d2SJianfeng Tan
640d4a586d2SJianfeng Tan RTE_REGISTER_BUS(vdev, rte_vdev_bus);
641eeded204SDavid Marchand RTE_LOG_REGISTER_DEFAULT(vdev_logtype_bus, NOTICE);
642