The device on auxiliary bus doesn't support being probed again without being removed firstly. The PMD will detect and return error for this unsupported operation. Some of the resources would be cleared wrongly. When quitting, there will be unexpected error like crash. To prevent this, the device driver will be checked before probing a device. Fixes: 1afce3086cf4 ("bus/auxiliary: introduce auxiliary bus") CC: stable@dpdk.org Signed-off-by: Bing Zhao <bingz@nvidia.com> Reviewed-by: Gregory Etelson <getelson@nvidia.com> Reviewed-by: Matan Azrad <matan@nvidia.com>
424 lines
9.5 KiB
C
424 lines
9.5 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (c) 2021 NVIDIA Corporation & Affiliates
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/queue.h>
|
|
#include <rte_errno.h>
|
|
#include <rte_interrupts.h>
|
|
#include <rte_log.h>
|
|
#include <bus_driver.h>
|
|
#include <rte_per_lcore.h>
|
|
#include <rte_memory.h>
|
|
#include <rte_eal.h>
|
|
#include <rte_eal_paging.h>
|
|
#include <rte_lcore.h>
|
|
#include <rte_string_fns.h>
|
|
#include <rte_common.h>
|
|
#include <rte_devargs.h>
|
|
|
|
#include "private.h"
|
|
|
|
static struct rte_devargs *
|
|
auxiliary_devargs_lookup(const char *name)
|
|
{
|
|
struct rte_devargs *devargs;
|
|
|
|
RTE_EAL_DEVARGS_FOREACH(RTE_BUS_AUXILIARY_NAME, devargs) {
|
|
if (strcmp(devargs->name, name) == 0)
|
|
return devargs;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Test whether the auxiliary device exist.
|
|
*
|
|
* Stub for OS not supporting auxiliary bus.
|
|
*/
|
|
__rte_weak bool
|
|
auxiliary_dev_exists(const char *name)
|
|
{
|
|
RTE_SET_USED(name);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Scan the devices in the auxiliary bus.
|
|
*
|
|
* Stub for OS not supporting auxiliary bus.
|
|
*/
|
|
__rte_weak int
|
|
auxiliary_scan(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Update a device's devargs being scanned.
|
|
*/
|
|
void
|
|
auxiliary_on_scan(struct rte_auxiliary_device *aux_dev)
|
|
{
|
|
aux_dev->device.devargs = auxiliary_devargs_lookup(aux_dev->name);
|
|
}
|
|
|
|
/*
|
|
* Match the auxiliary driver and device using driver function.
|
|
*/
|
|
bool
|
|
auxiliary_match(const struct rte_auxiliary_driver *aux_drv,
|
|
const struct rte_auxiliary_device *aux_dev)
|
|
{
|
|
if (aux_drv->match == NULL)
|
|
return false;
|
|
return aux_drv->match(aux_dev->name);
|
|
}
|
|
|
|
/*
|
|
* Call the probe() function of the driver.
|
|
*/
|
|
static int
|
|
rte_auxiliary_probe_one_driver(struct rte_auxiliary_driver *drv,
|
|
struct rte_auxiliary_device *dev)
|
|
{
|
|
enum rte_iova_mode iova_mode;
|
|
int ret;
|
|
|
|
if (drv == NULL || dev == NULL)
|
|
return -EINVAL;
|
|
|
|
/* Check if driver supports it. */
|
|
if (!auxiliary_match(drv, dev))
|
|
/* Match of device and driver failed */
|
|
return 1;
|
|
|
|
/* No initialization when marked as blocked, return without error. */
|
|
if (dev->device.devargs != NULL &&
|
|
dev->device.devargs->policy == RTE_DEV_BLOCKED) {
|
|
AUXILIARY_LOG(INFO, "Device is blocked, not initializing");
|
|
return -1;
|
|
}
|
|
|
|
if (dev->device.numa_node < 0 && rte_socket_count() > 1)
|
|
RTE_LOG(INFO, EAL, "Device %s is not NUMA-aware\n", dev->name);
|
|
|
|
if (rte_dev_is_probed(&dev->device)) {
|
|
RTE_LOG(DEBUG, EAL, "Device %s is already probed on auxiliary bus\n",
|
|
dev->device.name);
|
|
return -EEXIST;
|
|
}
|
|
|
|
iova_mode = rte_eal_iova_mode();
|
|
if ((drv->drv_flags & RTE_AUXILIARY_DRV_NEED_IOVA_AS_VA) > 0 &&
|
|
iova_mode != RTE_IOVA_VA) {
|
|
AUXILIARY_LOG(ERR, "Driver %s expecting VA IOVA mode but current mode is PA, not initializing",
|
|
drv->driver.name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Allocate interrupt instance */
|
|
dev->intr_handle =
|
|
rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
|
|
if (dev->intr_handle == NULL) {
|
|
AUXILIARY_LOG(ERR, "Could not allocate interrupt instance for device %s",
|
|
dev->name);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
dev->driver = drv;
|
|
|
|
AUXILIARY_LOG(INFO, "Probe auxiliary driver: %s device: %s (NUMA node %i)",
|
|
drv->driver.name, dev->name, dev->device.numa_node);
|
|
ret = drv->probe(drv, dev);
|
|
if (ret != 0) {
|
|
dev->driver = NULL;
|
|
rte_intr_instance_free(dev->intr_handle);
|
|
dev->intr_handle = NULL;
|
|
} else {
|
|
dev->device.driver = &drv->driver;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Call the remove() function of the driver.
|
|
*/
|
|
static int
|
|
rte_auxiliary_driver_remove_dev(struct rte_auxiliary_device *dev)
|
|
{
|
|
struct rte_auxiliary_driver *drv;
|
|
int ret = 0;
|
|
|
|
if (dev == NULL)
|
|
return -EINVAL;
|
|
|
|
drv = dev->driver;
|
|
|
|
AUXILIARY_LOG(DEBUG, "Driver %s remove auxiliary device %s on NUMA node %i",
|
|
drv->driver.name, dev->name, dev->device.numa_node);
|
|
|
|
if (drv->remove != NULL) {
|
|
ret = drv->remove(dev);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
/* clear driver structure */
|
|
dev->driver = NULL;
|
|
dev->device.driver = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Call the probe() function of all registered drivers for the given device.
|
|
* Return < 0 if initialization failed.
|
|
* Return 1 if no driver is found for this device.
|
|
*/
|
|
static int
|
|
auxiliary_probe_all_drivers(struct rte_auxiliary_device *dev)
|
|
{
|
|
struct rte_auxiliary_driver *drv;
|
|
int rc;
|
|
|
|
if (dev == NULL)
|
|
return -EINVAL;
|
|
|
|
FOREACH_DRIVER_ON_AUXILIARY_BUS(drv) {
|
|
if (!drv->match(dev->name))
|
|
continue;
|
|
|
|
rc = rte_auxiliary_probe_one_driver(drv, dev);
|
|
if (rc < 0)
|
|
/* negative value is an error */
|
|
return rc;
|
|
if (rc > 0)
|
|
/* positive value means driver doesn't support it */
|
|
continue;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Scan the content of the auxiliary bus, and call the probe function for
|
|
* all registered drivers to try to probe discovered devices.
|
|
*/
|
|
static int
|
|
auxiliary_probe(void)
|
|
{
|
|
struct rte_auxiliary_device *dev = NULL;
|
|
size_t probed = 0, failed = 0;
|
|
int ret = 0;
|
|
|
|
FOREACH_DEVICE_ON_AUXILIARY_BUS(dev) {
|
|
probed++;
|
|
|
|
ret = auxiliary_probe_all_drivers(dev);
|
|
if (ret < 0) {
|
|
if (ret != -EEXIST) {
|
|
AUXILIARY_LOG(ERR, "Requested device %s cannot be used",
|
|
dev->name);
|
|
rte_errno = errno;
|
|
failed++;
|
|
}
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
return (probed && probed == failed) ? -1 : 0;
|
|
}
|
|
|
|
static int
|
|
auxiliary_parse(const char *name, void *addr)
|
|
{
|
|
struct rte_auxiliary_driver *drv = NULL;
|
|
const char **out = addr;
|
|
|
|
/* Allow empty device name "auxiliary:" to bypass entire bus scan. */
|
|
if (strlen(name) == 0)
|
|
return 0;
|
|
|
|
FOREACH_DRIVER_ON_AUXILIARY_BUS(drv) {
|
|
if (drv->match(name))
|
|
break;
|
|
}
|
|
if (drv != NULL && addr != NULL)
|
|
*out = name;
|
|
return drv != NULL ? 0 : -1;
|
|
}
|
|
|
|
/* Register a driver */
|
|
void
|
|
rte_auxiliary_register(struct rte_auxiliary_driver *driver)
|
|
{
|
|
TAILQ_INSERT_TAIL(&auxiliary_bus.driver_list, driver, next);
|
|
}
|
|
|
|
/* Unregister a driver */
|
|
void
|
|
rte_auxiliary_unregister(struct rte_auxiliary_driver *driver)
|
|
{
|
|
TAILQ_REMOVE(&auxiliary_bus.driver_list, driver, next);
|
|
}
|
|
|
|
/* Add a device to auxiliary bus */
|
|
void
|
|
auxiliary_add_device(struct rte_auxiliary_device *aux_dev)
|
|
{
|
|
TAILQ_INSERT_TAIL(&auxiliary_bus.device_list, aux_dev, next);
|
|
}
|
|
|
|
/* Insert a device into a predefined position in auxiliary bus */
|
|
void
|
|
auxiliary_insert_device(struct rte_auxiliary_device *exist_aux_dev,
|
|
struct rte_auxiliary_device *new_aux_dev)
|
|
{
|
|
TAILQ_INSERT_BEFORE(exist_aux_dev, new_aux_dev, next);
|
|
}
|
|
|
|
/* Remove a device from auxiliary bus */
|
|
static void
|
|
rte_auxiliary_remove_device(struct rte_auxiliary_device *auxiliary_dev)
|
|
{
|
|
TAILQ_REMOVE(&auxiliary_bus.device_list, auxiliary_dev, next);
|
|
}
|
|
|
|
static struct rte_device *
|
|
auxiliary_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,
|
|
const void *data)
|
|
{
|
|
const struct rte_auxiliary_device *pstart;
|
|
struct rte_auxiliary_device *adev;
|
|
|
|
if (start != NULL) {
|
|
pstart = RTE_DEV_TO_AUXILIARY_CONST(start);
|
|
adev = TAILQ_NEXT(pstart, next);
|
|
} else {
|
|
adev = TAILQ_FIRST(&auxiliary_bus.device_list);
|
|
}
|
|
while (adev != NULL) {
|
|
if (cmp(&adev->device, data) == 0)
|
|
return &adev->device;
|
|
adev = TAILQ_NEXT(adev, next);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
auxiliary_plug(struct rte_device *dev)
|
|
{
|
|
if (!auxiliary_dev_exists(dev->name))
|
|
return -ENOENT;
|
|
return auxiliary_probe_all_drivers(RTE_DEV_TO_AUXILIARY(dev));
|
|
}
|
|
|
|
static int
|
|
auxiliary_unplug(struct rte_device *dev)
|
|
{
|
|
struct rte_auxiliary_device *adev;
|
|
int ret;
|
|
|
|
adev = RTE_DEV_TO_AUXILIARY(dev);
|
|
ret = rte_auxiliary_driver_remove_dev(adev);
|
|
if (ret == 0) {
|
|
rte_auxiliary_remove_device(adev);
|
|
rte_devargs_remove(dev->devargs);
|
|
rte_intr_instance_free(adev->intr_handle);
|
|
free(adev);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
auxiliary_dma_map(struct rte_device *dev, void *addr, uint64_t iova, size_t len)
|
|
{
|
|
struct rte_auxiliary_device *aux_dev = RTE_DEV_TO_AUXILIARY(dev);
|
|
|
|
if (dev == NULL || aux_dev->driver == NULL) {
|
|
rte_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (aux_dev->driver->dma_map == NULL) {
|
|
rte_errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
return aux_dev->driver->dma_map(aux_dev, addr, iova, len);
|
|
}
|
|
|
|
static int
|
|
auxiliary_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova,
|
|
size_t len)
|
|
{
|
|
struct rte_auxiliary_device *aux_dev = RTE_DEV_TO_AUXILIARY(dev);
|
|
|
|
if (dev == NULL || aux_dev->driver == NULL) {
|
|
rte_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (aux_dev->driver->dma_unmap == NULL) {
|
|
rte_errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
return aux_dev->driver->dma_unmap(aux_dev, addr, iova, len);
|
|
}
|
|
|
|
bool
|
|
auxiliary_is_ignored_device(const char *name)
|
|
{
|
|
struct rte_devargs *devargs = auxiliary_devargs_lookup(name);
|
|
|
|
switch (auxiliary_bus.bus.conf.scan_mode) {
|
|
case RTE_BUS_SCAN_ALLOWLIST:
|
|
if (devargs && devargs->policy == RTE_DEV_ALLOWED)
|
|
return false;
|
|
break;
|
|
case RTE_BUS_SCAN_UNDEFINED:
|
|
case RTE_BUS_SCAN_BLOCKLIST:
|
|
if (devargs == NULL || devargs->policy != RTE_DEV_BLOCKED)
|
|
return false;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static enum rte_iova_mode
|
|
auxiliary_get_iommu_class(void)
|
|
{
|
|
const struct rte_auxiliary_driver *drv;
|
|
|
|
FOREACH_DRIVER_ON_AUXILIARY_BUS(drv) {
|
|
if ((drv->drv_flags & RTE_AUXILIARY_DRV_NEED_IOVA_AS_VA) > 0)
|
|
return RTE_IOVA_VA;
|
|
}
|
|
|
|
return RTE_IOVA_DC;
|
|
}
|
|
|
|
struct rte_auxiliary_bus auxiliary_bus = {
|
|
.bus = {
|
|
.scan = auxiliary_scan,
|
|
.probe = auxiliary_probe,
|
|
.find_device = auxiliary_find_device,
|
|
.plug = auxiliary_plug,
|
|
.unplug = auxiliary_unplug,
|
|
.parse = auxiliary_parse,
|
|
.dma_map = auxiliary_dma_map,
|
|
.dma_unmap = auxiliary_dma_unmap,
|
|
.get_iommu_class = auxiliary_get_iommu_class,
|
|
.dev_iterate = auxiliary_dev_iterate,
|
|
},
|
|
.device_list = TAILQ_HEAD_INITIALIZER(auxiliary_bus.device_list),
|
|
.driver_list = TAILQ_HEAD_INITIALIZER(auxiliary_bus.driver_list),
|
|
};
|
|
|
|
RTE_REGISTER_BUS(auxiliary, auxiliary_bus.bus);
|
|
RTE_LOG_REGISTER_DEFAULT(auxiliary_bus_logtype, NOTICE);
|