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