These functions are used for attaching or detaching a port. When rte_eal_dev_attach() is called, the function tries to realize the device name as pci address. If this is done successfully, rte_eal_dev_attach() will attach physical device port. If not, attaches virtual devive port. When rte_eal_dev_detach() is called, the function gets the device type of this port to know whether the port is came from physical or virtual. And then specific detaching function will be called.
v5: - Change function names like below. rte_eal_dev_find_and_invoke() to rte_eal_vdev_find_and_invoke(). rte_eal_dev_invoke() to rte_eal_vdev_invoke(). - Add code to handle a return value of rte_eal_devargs_remove(). - Fix pci address format in rte_eal_dev_detach(). v4: - Fix comment. - Add error checking. - Fix indent of 'if' statement. - Change function name. Signed-off-by: Tetsuya Mukawa <mukawa at igel.co.jp> --- lib/librte_eal/common/eal_common_dev.c | 274 ++++++++++++++++++++++++++++++++ lib/librte_eal/common/eal_private.h | 11 ++ lib/librte_eal/common/include/rte_dev.h | 33 ++++ lib/librte_eal/linuxapp/eal/Makefile | 1 + lib/librte_eal/linuxapp/eal/eal_pci.c | 6 +- 5 files changed, 322 insertions(+), 3 deletions(-) diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..e3a3f54 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -32,10 +32,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <stdio.h> +#include <limits.h> #include <string.h> #include <inttypes.h> #include <sys/queue.h> +#include <rte_ethdev.h> #include <rte_dev.h> #include <rte_devargs.h> #include <rte_debug.h> @@ -107,3 +110,274 @@ rte_eal_dev_init(void) } return 0; } + +/* So far, DPDK hotplug function only supports linux */ +#ifdef ENABLE_HOTPLUG +static void +rte_eal_vdev_invoke(struct rte_driver *driver, + struct rte_devargs *devargs, enum rte_eal_invoke_type type) +{ + if ((driver == NULL) || (devargs == NULL)) + return; + + switch (type) { + case RTE_EAL_INVOKE_TYPE_PROBE: + driver->init(devargs->virtual.drv_name, devargs->args); + break; + case RTE_EAL_INVOKE_TYPE_CLOSE: + driver->uninit(devargs->virtual.drv_name, devargs->args); + break; + default: + break; + } +} + +static int +rte_eal_vdev_find_and_invoke(const char *name, int type) +{ + struct rte_devargs *devargs; + struct rte_driver *driver; + + if (name == NULL) + return -EINVAL; + + /* call the init function for each virtual device */ + TAILQ_FOREACH(devargs, &devargs_list, next) { + + if (devargs->type != RTE_DEVTYPE_VIRTUAL) + continue; + + if (strncmp(name, devargs->virtual.drv_name, strlen(name))) + continue; + + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_VDEV) + continue; + + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + rte_eal_vdev_invoke(driver, devargs, type); + break; + } + } + + if (driver == NULL) { + RTE_LOG(WARNING, EAL, "no driver found for %s\n", + devargs->virtual.drv_name); + } + return 0; + } + return 1; +} + +/* attach the new physical device, then store port_id of the device */ +static int +rte_eal_dev_attach_pdev(struct rte_pci_addr *addr, uint8_t *port_id) +{ + uint8_t new_port_id; + struct rte_eth_dev devs[RTE_MAX_ETHPORTS]; + + if ((addr == NULL) || (port_id == NULL)) + goto err; + + /* save current port status */ + rte_eth_dev_save(devs); + /* re-construct pci_device_list */ + if (rte_eal_pci_scan()) + goto err; + /* invoke probe func of the driver can handle the new device */ + if (rte_eal_pci_probe_one(addr)) + goto err; + /* get port_id enabled by above procedures */ + if (rte_eth_dev_get_changed_port(devs, &new_port_id)) + goto err; + + *port_id = new_port_id; + return 0; +err: + RTE_LOG(ERR, EAL, "Drver, cannot attach the device\n"); + return -1; +} + +/* detach the new physical device, then store pci_addr of the device */ +static int +rte_eal_dev_detach_pdev(uint8_t port_id, struct rte_pci_addr *addr) +{ + struct rte_pci_addr freed_addr; + struct rte_pci_addr vp; + + if (addr == NULL) + goto err; + + /* check whether the driver supports detach feature, or not */ + if (rte_eth_dev_check_detachable(port_id)) + goto err; + + /* get pci address by port id */ + if (rte_eth_dev_get_addr_by_port(port_id, &freed_addr)) + goto err; + + /* Zerod pci addr means the port comes from virtual device */ + vp.domain = vp.bus = vp.devid = vp.function = 0; + if (eal_compare_pci_addr(&vp, &freed_addr) == 0) + goto err; + + /* invoke close func of the driver, + * also remove the device from pci_device_list */ + if (rte_eal_pci_close_one(&freed_addr)) + goto err; + + *addr = freed_addr; + return 0; +err: + RTE_LOG(ERR, EAL, "Drver, cannot detach the device\n"); + return -1; +} + +static void +get_vdev_name(char *vdevargs) +{ + char *sep; + + if (vdevargs == NULL) + return; + + /* set the first ',' to '\0' to split name and arguments */ + sep = strchr(vdevargs, ','); + if (sep != NULL) + sep[0] = '\0'; +} + +/* attach the new virtual device, then store port_id of the device */ +static int +rte_eal_dev_attach_vdev(const char *vdevargs, uint8_t *port_id) +{ + char *args; + uint8_t new_port_id; + struct rte_eth_dev devs[RTE_MAX_ETHPORTS]; + + if ((vdevargs == NULL) || (port_id == NULL)) + goto err0; + + args = strdup(vdevargs); + if (args == NULL) + goto err0; + + /* save current port status */ + rte_eth_dev_save(devs); + /* add the vdevargs to devargs_list */ + if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL, args)) + goto err1; + /* parse vdevargs, then retrieve device name */ + get_vdev_name(args); + /* walk around dev_driver_list to find the driver of the device, + * then invoke probe function o the driver */ + if (rte_eal_vdev_find_and_invoke(args, RTE_EAL_INVOKE_TYPE_PROBE)) + goto err2; + /* get port_id enabled by above procedures */ + if (rte_eth_dev_get_changed_port(devs, &new_port_id)) + goto err2; + + free(args); + *port_id = new_port_id; + return 0; +err2: + rte_eal_devargs_remove(RTE_DEVTYPE_VIRTUAL, args); +err1: + free(args); +err0: + RTE_LOG(ERR, EAL, "Drver, cannot detach the device\n"); + return -1; +} + +/* detach the new virtual device, then store the name of the device */ +static int +rte_eal_dev_detach_vdev(uint8_t port_id, char *vdevname) +{ + char name[RTE_ETH_NAME_MAX_LEN]; + + if (vdevname == NULL) + goto err; + + /* check whether the driver supports detach feature, or not */ + if (rte_eth_dev_check_detachable(port_id)) + goto err; + + /* get device name by port id */ + if (rte_eth_dev_get_name_by_port(port_id, name)) + goto err; + /* walk around dev_driver_list to find the driver of the device, + * then invoke close function o the driver */ + if (rte_eal_vdev_find_and_invoke(name, RTE_EAL_INVOKE_TYPE_CLOSE)) + goto err; + /* remove the vdevname from devargs_list */ + if (rte_eal_devargs_remove(RTE_DEVTYPE_VIRTUAL, name)) + goto err; + + strncpy(vdevname, name, sizeof(name)); + return 0; +err: + RTE_LOG(ERR, EAL, "Drver, cannot detach the device\n"); + return -1; +} + +/* attach the new device, then store port_id of the device */ +int +rte_eal_dev_attach(const char *devargs, uint8_t *port_id) +{ + struct rte_pci_addr addr; + + if ((devargs == NULL) || (port_id == NULL)) + return -EINVAL; + + if (eal_parse_pci_DomBDF(devargs, &addr) == 0) + return rte_eal_dev_attach_pdev(&addr, port_id); + else + return rte_eal_dev_attach_vdev(devargs, port_id); +} + +/* detach the device, then store the name of the device */ +int +rte_eal_dev_detach(uint8_t port_id, char *name) +{ + struct rte_pci_addr addr; + int ret; + + if (name == NULL) + return -EINVAL; + + if (rte_eth_dev_get_device_type(port_id) == RTE_ETH_DEV_PHYSICAL) { + ret = rte_eth_dev_get_addr_by_port(port_id, &addr); + if (ret < 0) + return ret; + + ret = rte_eal_dev_detach_pdev(port_id, &addr); + if (ret == 0) + snprintf(name, RTE_ETH_NAME_MAX_LEN, + "%04x:%02x:%02x.%d", + addr.domain, addr.bus, + addr.devid, addr.function); + + return ret; + } else + return rte_eal_dev_detach_vdev(port_id, name); +} +#else /* ENABLE_HOTPLUG */ +int +rte_eal_dev_attach(const char *devargs __rte_unused, + uint8_t *port_id __rte_unused) +{ + RTE_LOG(ERR, EAL, "Hotplug support isn't enabled\n"); + return -1; +} + +/* detach the device, then store the name of the device */ +int +rte_eal_dev_detach(uint8_t port_id __rte_unused, + char *name __rte_unused) +{ + RTE_LOG(ERR, EAL, "Hotplug support isn't enabled\n"); + return -1; +} +#endif /* ENABLE_HOTPLUG */ diff --git a/lib/librte_eal/common/eal_private.h b/lib/librte_eal/common/eal_private.h index 1a362ab..8168a7a 100644 --- a/lib/librte_eal/common/eal_private.h +++ b/lib/librte_eal/common/eal_private.h @@ -163,6 +163,17 @@ enum rte_eal_invoke_type { }; /** + * Scan the content of the PCI bus, and the devices in the devices + * list + * + * This function is private to EAL. + * + * @return + * 0 on success, negative on error + */ +int rte_eal_pci_scan(void); + +/** * Mmap memory for single PCI device * * This function is private to EAL. diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..e63dd1c 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -47,6 +47,7 @@ extern "C" { #endif #include <sys/queue.h> +#include <rte_pci.h> /** Double linked list of device drivers. */ TAILQ_HEAD(rte_driver_list, rte_driver); @@ -57,6 +58,11 @@ TAILQ_HEAD(rte_driver_list, rte_driver); typedef int (rte_dev_init_t)(const char *name, const char *args); /** + * Uninitilization function called for each device driver once. + */ +typedef int (rte_dev_uninit_t)(const char *name, const char *args); + +/** * Driver type enumeration */ enum pmd_type { @@ -72,6 +78,7 @@ struct rte_driver { enum pmd_type type; /**< PMD Driver type */ const char *name; /**< Driver name. */ rte_dev_init_t *init; /**< Device init. function. */ + rte_dev_uninit_t *uninit; /**< Device uninit. function. */ }; /** @@ -93,6 +100,32 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** + * Attach a new device. + * + * @param devargs + * A pointer to a strings array describing the new device + * to be attached. The strings should be a pci address like + * '0000:01:00.0' or virtual device name like 'eth_pcap0'. + * @param port_id + * A pointer to a port identifier actually attached. + * @return + * 0 on success and port_id is filled, negative on error + */ +int rte_eal_dev_attach(const char *devargs, uint8_t *port_id); + +/** + * Detach a device. + * + * @param port_id + * The port identifier of the device to detach. + * @param addr + * A pointer to a device name actually detached. + * @return + * 0 on success and devname is filled, negative on error + */ +int rte_eal_dev_detach(uint8_t port_id, char *devname); + +/** * Initalize all the registered drivers in this process */ int rte_eal_dev_init(void); diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile index 72ecf3a..0ec83b5 100644 --- a/lib/librte_eal/linuxapp/eal/Makefile +++ b/lib/librte_eal/linuxapp/eal/Makefile @@ -41,6 +41,7 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include CFLAGS += -I$(RTE_SDK)/lib/librte_ring CFLAGS += -I$(RTE_SDK)/lib/librte_mempool CFLAGS += -I$(RTE_SDK)/lib/librte_malloc +CFLAGS += -I$(RTE_SDK)/lib/librte_mbuf CFLAGS += -I$(RTE_SDK)/lib/librte_ether CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem CFLAGS += -I$(RTE_SDK)/lib/librte_pmd_ring diff --git a/lib/librte_eal/linuxapp/eal/eal_pci.c b/lib/librte_eal/linuxapp/eal/eal_pci.c index 831422e..1f43688 100644 --- a/lib/librte_eal/linuxapp/eal/eal_pci.c +++ b/lib/librte_eal/linuxapp/eal/eal_pci.c @@ -431,8 +431,8 @@ error: * Scan the content of the PCI bus, and the devices in the devices * list */ -static int -pci_scan(void) +int +rte_eal_pci_scan(void) { struct dirent *e; DIR *dir; @@ -764,7 +764,7 @@ rte_eal_pci_init(void) if (internal_config.no_pci) return 0; - if (pci_scan() < 0) { + if (rte_eal_pci_scan() < 0) { RTE_LOG(ERR, EAL, "%s(): Cannot scan PCI bus\n", __func__); return -1; } -- 1.9.1