2018-01-29 13:11:25 +00:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
* Copyright(c) 2010-2014 Intel Corporation.
|
|
|
|
* Copyright(c) 2014 6WIND S.A.
|
2013-09-18 10:00:00 +00:00
|
|
|
*/
|
|
|
|
|
2015-02-25 19:32:25 +00:00
|
|
|
#include <stdio.h>
|
2014-03-01 12:14:34 +00:00
|
|
|
#include <string.h>
|
2013-09-18 10:00:00 +00:00
|
|
|
#include <inttypes.h>
|
2014-04-11 07:36:52 +00:00
|
|
|
#include <sys/queue.h>
|
|
|
|
|
2018-01-22 01:48:06 +00:00
|
|
|
#include <rte_compat.h>
|
2017-06-30 18:19:41 +00:00
|
|
|
#include <rte_bus.h>
|
2018-07-11 21:45:00 +00:00
|
|
|
#include <rte_class.h>
|
2014-04-21 14:59:31 +00:00
|
|
|
#include <rte_dev.h>
|
2014-04-11 07:36:52 +00:00
|
|
|
#include <rte_devargs.h>
|
2014-03-01 12:14:34 +00:00
|
|
|
#include <rte_debug.h>
|
2018-07-11 21:45:00 +00:00
|
|
|
#include <rte_errno.h>
|
|
|
|
#include <rte_kvargs.h>
|
2015-02-25 19:32:25 +00:00
|
|
|
#include <rte_log.h>
|
2018-04-13 08:30:38 +00:00
|
|
|
#include <rte_spinlock.h>
|
|
|
|
#include <rte_malloc.h>
|
2018-10-16 00:16:28 +00:00
|
|
|
#include <rte_string_fns.h>
|
2014-04-11 07:36:52 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
#include "eal_private.h"
|
2018-10-16 00:16:28 +00:00
|
|
|
#include "hotplug_mp.h"
|
2013-09-18 10:00:00 +00:00
|
|
|
|
2018-04-13 08:30:38 +00:00
|
|
|
/**
|
|
|
|
* The device event callback description.
|
|
|
|
*
|
|
|
|
* It contains callback address to be registered by user application,
|
|
|
|
* the pointer to the parameters for callback, and the device name.
|
|
|
|
*/
|
|
|
|
struct dev_event_callback {
|
|
|
|
TAILQ_ENTRY(dev_event_callback) next; /**< Callbacks list */
|
|
|
|
rte_dev_event_cb_fn cb_fn; /**< Callback address */
|
|
|
|
void *cb_arg; /**< Callback parameter */
|
|
|
|
char *dev_name; /**< Callback device name, NULL is for all device */
|
|
|
|
uint32_t active; /**< Callback is executing */
|
|
|
|
};
|
|
|
|
|
|
|
|
/** @internal Structure to keep track of registered callbacks */
|
|
|
|
TAILQ_HEAD(dev_event_cb_list, dev_event_callback);
|
|
|
|
|
|
|
|
/* The device event callback list for all registered callbacks. */
|
|
|
|
static struct dev_event_cb_list dev_event_cbs;
|
|
|
|
|
|
|
|
/* spinlock for device callbacks */
|
|
|
|
static rte_spinlock_t dev_event_lock = RTE_SPINLOCK_INITIALIZER;
|
|
|
|
|
2018-07-11 21:45:01 +00:00
|
|
|
struct dev_next_ctx {
|
|
|
|
struct rte_dev_iterator *it;
|
|
|
|
const char *bus_str;
|
|
|
|
const char *cls_str;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CTX(it, bus_str, cls_str) \
|
|
|
|
(&(const struct dev_next_ctx){ \
|
|
|
|
.it = it, \
|
|
|
|
.bus_str = bus_str, \
|
|
|
|
.cls_str = cls_str, \
|
|
|
|
})
|
|
|
|
|
|
|
|
#define ITCTX(ptr) \
|
|
|
|
(((struct dev_next_ctx *)(intptr_t)ptr)->it)
|
|
|
|
|
|
|
|
#define BUSCTX(ptr) \
|
|
|
|
(((struct dev_next_ctx *)(intptr_t)ptr)->bus_str)
|
|
|
|
|
|
|
|
#define CLSCTX(ptr) \
|
|
|
|
(((struct dev_next_ctx *)(intptr_t)ptr)->cls_str)
|
|
|
|
|
2017-06-30 18:19:41 +00:00
|
|
|
static int cmp_dev_name(const struct rte_device *dev, const void *_name)
|
|
|
|
{
|
|
|
|
const char *name = _name;
|
|
|
|
|
|
|
|
return strcmp(dev->name, name);
|
|
|
|
}
|
|
|
|
|
2018-09-25 20:55:27 +00:00
|
|
|
int __rte_experimental
|
|
|
|
rte_dev_is_probed(const struct rte_device *dev)
|
|
|
|
{
|
|
|
|
/* The field driver should be set only when the probe is successful. */
|
|
|
|
return dev->driver != NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-20 12:41:24 +00:00
|
|
|
int rte_eal_dev_attach(const char *name, const char *devargs)
|
|
|
|
{
|
2017-07-31 12:58:29 +00:00
|
|
|
struct rte_bus *bus;
|
2016-09-20 12:41:24 +00:00
|
|
|
|
|
|
|
if (name == NULL || devargs == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Invalid device or arguments provided\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-07-31 12:58:29 +00:00
|
|
|
bus = rte_bus_find_by_device_name(name);
|
|
|
|
if (bus == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Unable to find a bus for the device '%s'\n",
|
|
|
|
name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-11-07 06:54:20 +00:00
|
|
|
if (strcmp(bus->name, "pci") == 0 || strcmp(bus->name, "vdev") == 0)
|
|
|
|
return rte_eal_hotplug_add(bus->name, name, devargs);
|
2016-09-20 12:41:24 +00:00
|
|
|
|
2017-11-07 06:54:20 +00:00
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Device attach is only supported for PCI and vdev devices.\n");
|
|
|
|
|
|
|
|
return -ENOTSUP;
|
2016-09-20 12:41:24 +00:00
|
|
|
}
|
|
|
|
|
2017-06-30 18:19:42 +00:00
|
|
|
int rte_eal_dev_detach(struct rte_device *dev)
|
2016-09-20 12:41:24 +00:00
|
|
|
{
|
2017-06-30 18:19:42 +00:00
|
|
|
struct rte_bus *bus;
|
|
|
|
int ret;
|
2016-09-20 12:41:24 +00:00
|
|
|
|
2017-06-30 18:19:42 +00:00
|
|
|
if (dev == NULL) {
|
2016-09-20 12:41:24 +00:00
|
|
|
RTE_LOG(ERR, EAL, "Invalid device provided.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-30 18:19:42 +00:00
|
|
|
bus = rte_bus_find_by_device(dev);
|
|
|
|
if (bus == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Cannot find bus for device (%s)\n",
|
|
|
|
dev->name);
|
|
|
|
return -EINVAL;
|
2016-09-20 12:41:24 +00:00
|
|
|
}
|
|
|
|
|
2017-06-30 18:19:42 +00:00
|
|
|
if (bus->unplug == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Bus function not supported\n");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = bus->unplug(dev);
|
|
|
|
if (ret)
|
|
|
|
RTE_LOG(ERR, EAL, "Driver cannot detach the device (%s)\n",
|
|
|
|
dev->name);
|
|
|
|
return ret;
|
2016-09-20 12:41:24 +00:00
|
|
|
}
|
2017-06-30 18:19:41 +00:00
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
/* helper function to build devargs, caller should free the memory */
|
|
|
|
static int
|
|
|
|
build_devargs(const char *busname, const char *devname,
|
|
|
|
const char *drvargs, char **devargs)
|
2017-06-30 18:19:41 +00:00
|
|
|
{
|
2018-09-07 22:27:27 +00:00
|
|
|
int length;
|
2017-06-30 18:19:41 +00:00
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
length = snprintf(NULL, 0, "%s:%s,%s", busname, devname, drvargs);
|
|
|
|
if (length < 0)
|
|
|
|
return -EINVAL;
|
2018-10-16 00:16:28 +00:00
|
|
|
|
|
|
|
*devargs = malloc(length + 1);
|
|
|
|
if (*devargs == NULL)
|
2018-09-07 22:27:27 +00:00
|
|
|
return -ENOMEM;
|
2018-10-16 00:16:28 +00:00
|
|
|
|
|
|
|
length = snprintf(*devargs, length + 1, "%s:%s,%s",
|
|
|
|
busname, devname, drvargs);
|
|
|
|
if (length < 0) {
|
|
|
|
free(*devargs);
|
2018-09-07 22:27:27 +00:00
|
|
|
return -EINVAL;
|
2018-10-16 00:16:28 +00:00
|
|
|
}
|
2017-06-30 18:19:41 +00:00
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-09-07 22:27:27 +00:00
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
int
|
|
|
|
rte_eal_hotplug_add(const char *busname, const char *devname,
|
|
|
|
const char *drvargs)
|
|
|
|
{
|
|
|
|
|
|
|
|
char *devargs;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = build_devargs(busname, devname, drvargs, &devargs);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = rte_dev_probe(devargs);
|
2018-09-07 22:27:27 +00:00
|
|
|
free(devargs);
|
2018-10-16 00:16:28 +00:00
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
/* probe device at local process. */
|
|
|
|
int
|
|
|
|
local_dev_probe(const char *devargs, struct rte_device **new_dev)
|
2018-09-07 22:27:27 +00:00
|
|
|
{
|
|
|
|
struct rte_device *dev;
|
|
|
|
struct rte_devargs *da;
|
|
|
|
int ret;
|
2017-06-30 18:19:41 +00:00
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
*new_dev = NULL;
|
2017-07-15 17:56:38 +00:00
|
|
|
da = calloc(1, sizeof(*da));
|
2018-04-23 23:54:49 +00:00
|
|
|
if (da == NULL)
|
|
|
|
return -ENOMEM;
|
2017-07-15 17:56:38 +00:00
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
ret = rte_devargs_parse(da, devargs);
|
2017-07-15 17:56:38 +00:00
|
|
|
if (ret)
|
|
|
|
goto err_devarg;
|
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
if (da->bus->plug == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Function plug not supported by bus (%s)\n",
|
|
|
|
da->bus->name);
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
goto err_devarg;
|
|
|
|
}
|
|
|
|
|
2018-04-23 23:54:51 +00:00
|
|
|
ret = rte_devargs_insert(da);
|
2017-07-15 17:56:38 +00:00
|
|
|
if (ret)
|
|
|
|
goto err_devarg;
|
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
ret = da->bus->scan();
|
2017-06-30 18:19:41 +00:00
|
|
|
if (ret)
|
2017-07-15 17:56:38 +00:00
|
|
|
goto err_devarg;
|
2017-06-30 18:19:41 +00:00
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
dev = da->bus->find_device(NULL, cmp_dev_name, da->name);
|
2017-06-30 18:19:41 +00:00
|
|
|
if (dev == NULL) {
|
2018-07-12 14:01:41 +00:00
|
|
|
RTE_LOG(ERR, EAL, "Cannot find device (%s)\n",
|
2018-09-07 22:27:27 +00:00
|
|
|
da->name);
|
2017-07-15 17:56:38 +00:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto err_devarg;
|
2017-06-30 18:19:41 +00:00
|
|
|
}
|
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
ret = dev->bus->plug(dev);
|
2017-07-15 17:56:38 +00:00
|
|
|
if (ret) {
|
eal: allow probing a device again
In the devargs syntax for device representors, it is possible to add
several devices at once: -w dbdf,representor=[0-3]
It will become a more frequent case when introducing wildcards
and ranges in the new devargs syntax.
If a devargs string is provided for probing, and updated with a bigger
range for a new probing, then we do not want it to fail because
part of this range was already probed previously.
There can be new ports to create from an existing rte_device.
That's why the check for an already probed device
is moved as bus responsibility.
In the case of vdev, a global check is kept in insert_vdev(),
assuming that a vdev will always have only one port.
In the case of ifpga and vmbus, already probed devices are checked.
In the case of NXP buses, the probing is done only once (no hotplug),
though a check is added at bus level for consistency.
In the case of PCI, a driver flag is added to allow PMD probing again.
Only the PMD knows the ports attached to one rte_device.
As another consequence of being able to probe in several steps,
the field rte_device.devargs must not be considered as a full
representation of the rte_device, but only the latest probing args.
Anyway, the field rte_device.devargs is used only for probing.
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
Reviewed-by: Andrew Rybchenko <arybchenko@solarflare.com>
Tested-by: Andrew Rybchenko <arybchenko@solarflare.com>
Acked-by: Shreyansh Jain <shreyansh.jain@nxp.com>
2018-09-19 23:37:14 +00:00
|
|
|
if (rte_dev_is_probed(dev)) /* if already succeeded earlier */
|
|
|
|
return ret; /* no rollback */
|
2017-06-30 18:19:41 +00:00
|
|
|
RTE_LOG(ERR, EAL, "Driver cannot attach the device (%s)\n",
|
|
|
|
dev->name);
|
2017-07-15 17:56:38 +00:00
|
|
|
goto err_devarg;
|
|
|
|
}
|
2018-10-16 00:16:28 +00:00
|
|
|
|
|
|
|
*new_dev = dev;
|
2017-07-15 17:56:38 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_devarg:
|
2018-09-19 21:55:01 +00:00
|
|
|
if (rte_devargs_remove(da) != 0) {
|
2017-08-03 12:34:31 +00:00
|
|
|
free(da->args);
|
|
|
|
free(da);
|
|
|
|
}
|
2017-06-30 18:19:41 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
int __rte_experimental
|
|
|
|
rte_dev_probe(const char *devargs)
|
|
|
|
{
|
|
|
|
struct eal_dev_mp_req req;
|
|
|
|
struct rte_device *dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.t = EAL_DEV_REQ_TYPE_ATTACH;
|
|
|
|
strlcpy(req.devargs, devargs, EAL_DEV_MP_DEV_ARGS_MAX_LEN);
|
|
|
|
|
|
|
|
if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
|
|
|
|
/**
|
|
|
|
* If in secondary process, just send IPC request to
|
|
|
|
* primary process.
|
|
|
|
*/
|
|
|
|
ret = eal_dev_hotplug_request_to_primary(&req);
|
|
|
|
if (ret != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to send hotplug request to primary\n");
|
|
|
|
return -ENOMSG;
|
|
|
|
}
|
|
|
|
if (req.result != 0)
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to hotplug add device\n");
|
|
|
|
return req.result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* attach a shared device from primary start from here: */
|
|
|
|
|
|
|
|
/* primary attach the new device itself. */
|
|
|
|
ret = local_dev_probe(devargs, &dev);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to attach device on primary process\n");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* it is possible that secondary process failed to attached a
|
|
|
|
* device that primary process have during initialization,
|
|
|
|
* so for -EEXIST case, we still need to sync with secondary
|
|
|
|
* process.
|
|
|
|
*/
|
|
|
|
if (ret != -EEXIST)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* primary send attach sync request to secondary. */
|
|
|
|
ret = eal_dev_hotplug_request_to_secondary(&req);
|
|
|
|
|
|
|
|
/* if any communication error, we need to rollback. */
|
|
|
|
if (ret != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to send hotplug add request to secondary\n");
|
|
|
|
ret = -ENOMSG;
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* if any secondary failed to attach, we need to consider if rollback
|
|
|
|
* is necessary.
|
|
|
|
*/
|
|
|
|
if (req.result != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to attach device on secondary process\n");
|
|
|
|
ret = req.result;
|
|
|
|
|
|
|
|
/* for -EEXIST, we don't need to rollback. */
|
|
|
|
if (ret == -EEXIST)
|
|
|
|
return ret;
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rollback:
|
|
|
|
req.t = EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK;
|
|
|
|
|
|
|
|
/* primary send rollback request to secondary. */
|
|
|
|
if (eal_dev_hotplug_request_to_secondary(&req) != 0)
|
|
|
|
RTE_LOG(WARNING, EAL,
|
|
|
|
"Failed to rollback device attach on secondary."
|
|
|
|
"Devices in secondary may not sync with primary\n");
|
|
|
|
|
|
|
|
/* primary rollback itself. */
|
|
|
|
if (local_dev_remove(dev) != 0)
|
|
|
|
RTE_LOG(WARNING, EAL,
|
|
|
|
"Failed to rollback device attach on primary."
|
|
|
|
"Devices in secondary may not sync with primary\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-26 21:22:27 +00:00
|
|
|
int
|
2018-01-22 01:48:06 +00:00
|
|
|
rte_eal_hotplug_remove(const char *busname, const char *devname)
|
2017-06-30 18:19:41 +00:00
|
|
|
{
|
|
|
|
struct rte_device *dev;
|
2018-09-07 22:27:27 +00:00
|
|
|
struct rte_bus *bus;
|
2017-06-30 18:19:41 +00:00
|
|
|
|
|
|
|
bus = rte_bus_find_by_name(busname);
|
|
|
|
if (bus == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", busname);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = bus->find_device(NULL, cmp_dev_name, devname);
|
|
|
|
if (dev == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", devname);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-09-07 22:27:27 +00:00
|
|
|
return rte_dev_remove(dev);
|
|
|
|
}
|
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
/* remove device at local process. */
|
|
|
|
int
|
|
|
|
local_dev_remove(struct rte_device *dev)
|
2018-09-07 22:27:27 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (dev->bus->unplug == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Function unplug not supported by bus (%s)\n",
|
|
|
|
dev->bus->name);
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = dev->bus->unplug(dev);
|
2018-10-16 00:16:28 +00:00
|
|
|
if (ret) {
|
2017-06-30 18:19:41 +00:00
|
|
|
RTE_LOG(ERR, EAL, "Driver cannot detach the device (%s)\n",
|
|
|
|
dev->name);
|
2018-10-16 00:16:28 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __rte_experimental
|
|
|
|
rte_dev_remove(struct rte_device *dev)
|
|
|
|
{
|
|
|
|
struct eal_dev_mp_req req;
|
|
|
|
char *devargs;
|
|
|
|
int ret;
|
|
|
|
|
2018-09-25 20:55:27 +00:00
|
|
|
if (!rte_dev_is_probed(dev)) {
|
|
|
|
RTE_LOG(ERR, EAL, "Device is not probed\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2018-10-16 00:16:28 +00:00
|
|
|
ret = build_devargs(dev->devargs->bus->name, dev->name, "", &devargs);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.t = EAL_DEV_REQ_TYPE_DETACH;
|
|
|
|
strlcpy(req.devargs, devargs, EAL_DEV_MP_DEV_ARGS_MAX_LEN);
|
|
|
|
free(devargs);
|
|
|
|
|
|
|
|
if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
|
|
|
|
/**
|
|
|
|
* If in secondary process, just send IPC request to
|
|
|
|
* primary process.
|
|
|
|
*/
|
|
|
|
ret = eal_dev_hotplug_request_to_primary(&req);
|
|
|
|
if (ret != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to send hotplug request to primary\n");
|
|
|
|
return -ENOMSG;
|
|
|
|
}
|
|
|
|
if (req.result != 0)
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to hotplug remove device\n");
|
|
|
|
return req.result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* detach a device from primary start from here: */
|
|
|
|
|
|
|
|
/* primary send detach sync request to secondary */
|
|
|
|
ret = eal_dev_hotplug_request_to_secondary(&req);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* if communication error, we need to rollback, because it is possible
|
|
|
|
* part of the secondary processes still detached it successfully.
|
|
|
|
*/
|
|
|
|
if (ret != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to send device detach request to secondary\n");
|
|
|
|
ret = -ENOMSG;
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* if any secondary failed to detach, we need to consider if rollback
|
|
|
|
* is necessary.
|
|
|
|
*/
|
|
|
|
if (req.result != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to detach device on secondary process\n");
|
|
|
|
ret = req.result;
|
|
|
|
/**
|
|
|
|
* if -ENOENT, we don't need to rollback, since devices is
|
|
|
|
* already detached on secondary process.
|
|
|
|
*/
|
|
|
|
if (ret != -ENOENT)
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* primary detach the device itself. */
|
|
|
|
ret = local_dev_remove(dev);
|
|
|
|
|
|
|
|
/* if primary failed, still need to consider if rollback is necessary */
|
|
|
|
if (ret != 0) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to detach device on primary process\n");
|
|
|
|
/* if -ENOENT, we don't need to rollback */
|
|
|
|
if (ret == -ENOENT)
|
|
|
|
return ret;
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rollback:
|
|
|
|
req.t = EAL_DEV_REQ_TYPE_DETACH_ROLLBACK;
|
|
|
|
|
|
|
|
/* primary send rollback request to secondary. */
|
|
|
|
if (eal_dev_hotplug_request_to_secondary(&req) != 0)
|
|
|
|
RTE_LOG(WARNING, EAL,
|
|
|
|
"Failed to rollback device detach on secondary."
|
|
|
|
"Devices in secondary may not sync with primary\n");
|
|
|
|
|
2017-06-30 18:19:41 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2018-04-13 08:30:38 +00:00
|
|
|
|
|
|
|
int __rte_experimental
|
|
|
|
rte_dev_event_callback_register(const char *device_name,
|
|
|
|
rte_dev_event_cb_fn cb_fn,
|
|
|
|
void *cb_arg)
|
|
|
|
{
|
|
|
|
struct dev_event_callback *event_cb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!cb_fn)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rte_spinlock_lock(&dev_event_lock);
|
|
|
|
|
|
|
|
if (TAILQ_EMPTY(&dev_event_cbs))
|
|
|
|
TAILQ_INIT(&dev_event_cbs);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(event_cb, &dev_event_cbs, next) {
|
|
|
|
if (event_cb->cb_fn == cb_fn && event_cb->cb_arg == cb_arg) {
|
|
|
|
if (device_name == NULL && event_cb->dev_name == NULL)
|
|
|
|
break;
|
|
|
|
if (device_name == NULL || event_cb->dev_name == NULL)
|
|
|
|
continue;
|
|
|
|
if (!strcmp(event_cb->dev_name, device_name))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create a new callback. */
|
|
|
|
if (event_cb == NULL) {
|
|
|
|
event_cb = malloc(sizeof(struct dev_event_callback));
|
|
|
|
if (event_cb != NULL) {
|
|
|
|
event_cb->cb_fn = cb_fn;
|
|
|
|
event_cb->cb_arg = cb_arg;
|
|
|
|
event_cb->active = 0;
|
|
|
|
if (!device_name) {
|
|
|
|
event_cb->dev_name = NULL;
|
|
|
|
} else {
|
|
|
|
event_cb->dev_name = strdup(device_name);
|
|
|
|
if (event_cb->dev_name == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TAILQ_INSERT_TAIL(&dev_event_cbs, event_cb, next);
|
|
|
|
} else {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Failed to allocate memory for device "
|
|
|
|
"event callback.");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"The callback is already exist, no need "
|
|
|
|
"to register again.\n");
|
|
|
|
ret = -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
rte_spinlock_unlock(&dev_event_lock);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
free(event_cb);
|
|
|
|
rte_spinlock_unlock(&dev_event_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __rte_experimental
|
|
|
|
rte_dev_event_callback_unregister(const char *device_name,
|
|
|
|
rte_dev_event_cb_fn cb_fn,
|
|
|
|
void *cb_arg)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct dev_event_callback *event_cb, *next;
|
|
|
|
|
|
|
|
if (!cb_fn)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rte_spinlock_lock(&dev_event_lock);
|
|
|
|
/*walk through the callbacks and remove all that match. */
|
|
|
|
for (event_cb = TAILQ_FIRST(&dev_event_cbs); event_cb != NULL;
|
|
|
|
event_cb = next) {
|
|
|
|
|
|
|
|
next = TAILQ_NEXT(event_cb, next);
|
|
|
|
|
|
|
|
if (device_name != NULL && event_cb->dev_name != NULL) {
|
|
|
|
if (!strcmp(event_cb->dev_name, device_name)) {
|
|
|
|
if (event_cb->cb_fn != cb_fn ||
|
|
|
|
(cb_arg != (void *)-1 &&
|
|
|
|
event_cb->cb_arg != cb_arg))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (device_name != NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if this callback is not executing right now,
|
|
|
|
* then remove it.
|
|
|
|
*/
|
|
|
|
if (event_cb->active == 0) {
|
|
|
|
TAILQ_REMOVE(&dev_event_cbs, event_cb, next);
|
|
|
|
free(event_cb);
|
|
|
|
ret++;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rte_spinlock_unlock(&dev_event_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-10-04 06:44:41 +00:00
|
|
|
void __rte_experimental
|
|
|
|
rte_dev_event_callback_process(const char *device_name,
|
|
|
|
enum rte_dev_event_type event)
|
2018-04-13 08:30:38 +00:00
|
|
|
{
|
|
|
|
struct dev_event_callback *cb_lst;
|
|
|
|
|
|
|
|
if (device_name == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rte_spinlock_lock(&dev_event_lock);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(cb_lst, &dev_event_cbs, next) {
|
|
|
|
if (cb_lst->dev_name) {
|
|
|
|
if (strcmp(cb_lst->dev_name, device_name))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
cb_lst->active = 1;
|
|
|
|
rte_spinlock_unlock(&dev_event_lock);
|
|
|
|
cb_lst->cb_fn(device_name, event,
|
|
|
|
cb_lst->cb_arg);
|
|
|
|
rte_spinlock_lock(&dev_event_lock);
|
|
|
|
cb_lst->active = 0;
|
|
|
|
}
|
|
|
|
rte_spinlock_unlock(&dev_event_lock);
|
|
|
|
}
|
2018-07-11 21:45:00 +00:00
|
|
|
|
|
|
|
__rte_experimental
|
|
|
|
int
|
|
|
|
rte_dev_iterator_init(struct rte_dev_iterator *it,
|
|
|
|
const char *dev_str)
|
|
|
|
{
|
|
|
|
struct rte_devargs devargs;
|
|
|
|
struct rte_class *cls = NULL;
|
|
|
|
struct rte_bus *bus = NULL;
|
|
|
|
|
|
|
|
/* Having both bus_str and cls_str NULL is illegal,
|
|
|
|
* marking this iterator as invalid unless
|
|
|
|
* everything goes well.
|
|
|
|
*/
|
|
|
|
it->bus_str = NULL;
|
|
|
|
it->cls_str = NULL;
|
|
|
|
|
|
|
|
devargs.data = dev_str;
|
|
|
|
if (rte_devargs_layers_parse(&devargs, dev_str))
|
|
|
|
goto get_out;
|
|
|
|
|
|
|
|
bus = devargs.bus;
|
|
|
|
cls = devargs.cls;
|
|
|
|
/* The string should have at least
|
|
|
|
* one layer specified.
|
|
|
|
*/
|
|
|
|
if (bus == NULL && cls == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL,
|
|
|
|
"Either bus or class must be specified.\n");
|
|
|
|
rte_errno = EINVAL;
|
|
|
|
goto get_out;
|
|
|
|
}
|
|
|
|
if (bus != NULL && bus->dev_iterate == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Bus %s not supported\n", bus->name);
|
|
|
|
rte_errno = ENOTSUP;
|
|
|
|
goto get_out;
|
|
|
|
}
|
|
|
|
if (cls != NULL && cls->dev_iterate == NULL) {
|
|
|
|
RTE_LOG(ERR, EAL, "Class %s not supported\n", cls->name);
|
|
|
|
rte_errno = ENOTSUP;
|
|
|
|
goto get_out;
|
|
|
|
}
|
|
|
|
it->bus_str = devargs.bus_str;
|
|
|
|
it->cls_str = devargs.cls_str;
|
|
|
|
it->dev_str = dev_str;
|
|
|
|
it->bus = bus;
|
|
|
|
it->cls = cls;
|
|
|
|
it->device = NULL;
|
|
|
|
it->class_device = NULL;
|
|
|
|
get_out:
|
|
|
|
return -rte_errno;
|
|
|
|
}
|
2018-07-11 21:45:01 +00:00
|
|
|
|
|
|
|
static char *
|
|
|
|
dev_str_sane_copy(const char *str)
|
|
|
|
{
|
|
|
|
size_t end;
|
|
|
|
char *copy;
|
|
|
|
|
|
|
|
end = strcspn(str, ",/");
|
|
|
|
if (str[end] == ',') {
|
|
|
|
copy = strdup(&str[end + 1]);
|
|
|
|
} else {
|
|
|
|
/* '/' or '\0' */
|
|
|
|
copy = strdup("");
|
|
|
|
}
|
|
|
|
if (copy == NULL) {
|
|
|
|
rte_errno = ENOMEM;
|
|
|
|
} else {
|
|
|
|
char *slash;
|
|
|
|
|
|
|
|
slash = strchr(copy, '/');
|
|
|
|
if (slash != NULL)
|
|
|
|
slash[0] = '\0';
|
|
|
|
}
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
class_next_dev_cmp(const struct rte_class *cls,
|
|
|
|
const void *ctx)
|
|
|
|
{
|
|
|
|
struct rte_dev_iterator *it;
|
|
|
|
const char *cls_str = NULL;
|
|
|
|
void *dev;
|
|
|
|
|
|
|
|
if (cls->dev_iterate == NULL)
|
|
|
|
return 1;
|
|
|
|
it = ITCTX(ctx);
|
|
|
|
cls_str = CLSCTX(ctx);
|
|
|
|
dev = it->class_device;
|
|
|
|
/* it->cls_str != NULL means a class
|
|
|
|
* was specified in the devstr.
|
|
|
|
*/
|
|
|
|
if (it->cls_str != NULL && cls != it->cls)
|
|
|
|
return 1;
|
|
|
|
/* If an error occurred previously,
|
|
|
|
* no need to test further.
|
|
|
|
*/
|
|
|
|
if (rte_errno != 0)
|
|
|
|
return -1;
|
|
|
|
dev = cls->dev_iterate(dev, cls_str, it);
|
|
|
|
it->class_device = dev;
|
|
|
|
return dev == NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bus_next_dev_cmp(const struct rte_bus *bus,
|
|
|
|
const void *ctx)
|
|
|
|
{
|
|
|
|
struct rte_device *dev = NULL;
|
|
|
|
struct rte_class *cls = NULL;
|
|
|
|
struct rte_dev_iterator *it;
|
|
|
|
const char *bus_str = NULL;
|
|
|
|
|
|
|
|
if (bus->dev_iterate == NULL)
|
|
|
|
return 1;
|
|
|
|
it = ITCTX(ctx);
|
|
|
|
bus_str = BUSCTX(ctx);
|
|
|
|
dev = it->device;
|
|
|
|
/* it->bus_str != NULL means a bus
|
|
|
|
* was specified in the devstr.
|
|
|
|
*/
|
|
|
|
if (it->bus_str != NULL && bus != it->bus)
|
|
|
|
return 1;
|
|
|
|
/* If an error occurred previously,
|
|
|
|
* no need to test further.
|
|
|
|
*/
|
|
|
|
if (rte_errno != 0)
|
|
|
|
return -1;
|
|
|
|
if (it->cls_str == NULL) {
|
|
|
|
dev = bus->dev_iterate(dev, bus_str, it);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
/* cls_str != NULL */
|
|
|
|
if (dev == NULL) {
|
|
|
|
next_dev_on_bus:
|
|
|
|
dev = bus->dev_iterate(dev, bus_str, it);
|
|
|
|
it->device = dev;
|
|
|
|
}
|
|
|
|
if (dev == NULL)
|
|
|
|
return 1;
|
|
|
|
if (it->cls != NULL)
|
|
|
|
cls = TAILQ_PREV(it->cls, rte_class_list, next);
|
|
|
|
cls = rte_class_find(cls, class_next_dev_cmp, ctx);
|
|
|
|
if (cls != NULL) {
|
|
|
|
it->cls = cls;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
goto next_dev_on_bus;
|
|
|
|
end:
|
|
|
|
it->device = dev;
|
|
|
|
return dev == NULL;
|
|
|
|
}
|
|
|
|
__rte_experimental
|
|
|
|
struct rte_device *
|
|
|
|
rte_dev_iterator_next(struct rte_dev_iterator *it)
|
|
|
|
{
|
|
|
|
struct rte_bus *bus = NULL;
|
|
|
|
int old_errno = rte_errno;
|
|
|
|
char *bus_str = NULL;
|
|
|
|
char *cls_str = NULL;
|
|
|
|
|
|
|
|
rte_errno = 0;
|
|
|
|
if (it->bus_str == NULL && it->cls_str == NULL) {
|
|
|
|
/* Invalid iterator. */
|
|
|
|
rte_errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (it->bus != NULL)
|
|
|
|
bus = TAILQ_PREV(it->bus, rte_bus_list, next);
|
|
|
|
if (it->bus_str != NULL) {
|
|
|
|
bus_str = dev_str_sane_copy(it->bus_str);
|
|
|
|
if (bus_str == NULL)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (it->cls_str != NULL) {
|
|
|
|
cls_str = dev_str_sane_copy(it->cls_str);
|
|
|
|
if (cls_str == NULL)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
while ((bus = rte_bus_find(bus, bus_next_dev_cmp,
|
|
|
|
CTX(it, bus_str, cls_str)))) {
|
|
|
|
if (it->device != NULL) {
|
|
|
|
it->bus = bus;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (it->bus_str != NULL ||
|
|
|
|
rte_errno != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rte_errno == 0)
|
|
|
|
rte_errno = old_errno;
|
|
|
|
out:
|
|
|
|
free(bus_str);
|
|
|
|
free(cls_str);
|
|
|
|
return it->device;
|
|
|
|
}
|