ethdev: attach or detach port

These functions are used for attaching or detaching a port.
When rte_eth_dev_attach() is called, the function tries to realize the
device name as pci address. If this is done successfully,
rte_eth_dev_attach() will attach physical device port. If not, attaches
virtual devive port.
When rte_eth_dev_detach() is called, the function gets the device type
of this port to know whether the port is come from physical or virtual.
And then specific detaching function will be called.

Signed-off-by: Tetsuya Mukawa <mukawa@igel.co.jp>
This commit is contained in:
Tetsuya Mukawa 2015-02-26 04:32:26 +09:00 committed by Thomas Monjalon
parent 9f1653e7b7
commit 92d94d3744
6 changed files with 355 additions and 5 deletions

View File

@ -312,6 +312,15 @@ rte_eal_compare_pci_addr(struct rte_pci_addr *addr, struct rte_pci_addr *addr2)
return 0;
}
/**
* Scan the content of the PCI bus, and the devices in the devices
* list
*
* @return
* 0 on success, negative on error
*/
int rte_eal_pci_scan(void);
/**
* Probe the PCI bus for registered drivers.
*

View File

@ -440,8 +440,8 @@ parse_pci_addr_format(const char *buf, int bufsize, uint16_t *domain,
* 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;
@ -773,7 +773,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;
}

View File

@ -44,6 +44,7 @@ DPDK_2.0 {
rte_eal_pci_probe;
rte_eal_pci_probe_one;
rte_eal_pci_register;
rte_eal_pci_scan;
rte_eal_pci_unregister;
rte_eal_process_type;
rte_eal_remote_launch;

View File

@ -201,7 +201,7 @@ rte_eth_dev_data_alloc(void)
RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data));
}
static struct rte_eth_dev *
struct rte_eth_dev *
rte_eth_dev_allocated(const char *name)
{
unsigned i;
@ -270,7 +270,6 @@ rte_eth_dev_create_unique_device_name(char *name, size_t size,
pci_dev->addr.function);
if (ret < 0)
return ret;
return 0;
}
@ -427,6 +426,306 @@ rte_eth_dev_count(void)
return (nb_ports);
}
static enum rte_eth_dev_type
rte_eth_dev_get_device_type(uint8_t port_id)
{
if (!rte_eth_dev_is_valid_port(port_id))
return -1;
return rte_eth_devices[port_id].dev_type;
}
static int
rte_eth_dev_save(struct rte_eth_dev *devs, size_t size)
{
if ((devs == NULL) ||
(size != sizeof(struct rte_eth_dev) * RTE_MAX_ETHPORTS))
return -EINVAL;
/* save current rte_eth_devices */
memcpy(devs, rte_eth_devices, size);
return 0;
}
static int
rte_eth_dev_get_changed_port(struct rte_eth_dev *devs, uint8_t *port_id)
{
if ((devs == NULL) || (port_id == NULL))
return -EINVAL;
/* check which port was attached or detached */
for (*port_id = 0; *port_id < RTE_MAX_ETHPORTS; (*port_id)++, devs++) {
if (rte_eth_devices[*port_id].attached ^ devs->attached)
return 0;
}
return -ENODEV;
}
static int
rte_eth_dev_get_addr_by_port(uint8_t port_id, struct rte_pci_addr *addr)
{
if (!rte_eth_dev_is_valid_port(port_id)) {
PMD_DEBUG_TRACE("Invalid port_id=%d\n", port_id);
return -EINVAL;
}
if (addr == NULL) {
PMD_DEBUG_TRACE("Null pointer is specified\n");
return -EINVAL;
}
*addr = rte_eth_devices[port_id].pci_dev->addr;
return 0;
}
static int
rte_eth_dev_get_name_by_port(uint8_t port_id, char *name)
{
char *tmp;
if (!rte_eth_dev_is_valid_port(port_id)) {
PMD_DEBUG_TRACE("Invalid port_id=%d\n", port_id);
return -EINVAL;
}
if (name == NULL) {
PMD_DEBUG_TRACE("Null pointer is specified\n");
return -EINVAL;
}
/* shouldn't check 'rte_eth_devices[i].data',
* because it might be overwritten by VDEV PMD */
tmp = rte_eth_dev_data[port_id].name;
strcpy(name, tmp);
return 0;
}
static int
rte_eth_dev_is_detachable(uint8_t port_id)
{
uint32_t drv_flags;
if (port_id >= RTE_MAX_ETHPORTS) {
PMD_DEBUG_TRACE("Invalid port_id=%d\n", port_id);
return -EINVAL;
}
if (rte_eth_devices[port_id].dev_type == RTE_ETH_DEV_PCI) {
switch (rte_eth_devices[port_id].pci_dev->pt_driver) {
case RTE_PT_IGB_UIO:
case RTE_PT_UIO_GENERIC:
break;
case RTE_PT_VFIO:
default:
return -ENOTSUP;
}
}
drv_flags = rte_eth_devices[port_id].driver->pci_drv.drv_flags;
return !(drv_flags & RTE_PCI_DRV_DETACHABLE);
}
/* So far, DPDK hotplug function only supports linux */
#ifdef RTE_LIBRTE_EAL_HOTPLUG
/* attach the new physical device, then store port_id of the device */
static int
rte_eth_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 */
if (rte_eth_dev_save(devs, sizeof(devs)))
goto err;
/* re-construct pci_device_list */
if (rte_eal_pci_scan())
goto err;
/* invoke probe func of the driver can handle the new device.
* TODO:
* rte_eal_pci_probe_one() should return port_id.
* And rte_eth_dev_save() and rte_eth_dev_get_changed_port()
* should be removed. */
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, "Driver, cannot attach the device\n");
return -1;
}
/* detach the new physical device, then store pci_addr of the device */
static int
rte_eth_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_is_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 (rte_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, "Driver, cannot detach the device\n");
return -1;
}
/* attach the new virtual device, then store port_id of the device */
static int
rte_eth_dev_attach_vdev(const char *vdevargs, uint8_t *port_id)
{
char *name = NULL, *args = NULL;
uint8_t new_port_id;
struct rte_eth_dev devs[RTE_MAX_ETHPORTS];
int ret = -1;
if ((vdevargs == NULL) || (port_id == NULL))
goto end;
/* parse vdevargs, then retrieve device name and args */
if (rte_eal_parse_devargs_str(vdevargs, &name, &args))
goto end;
/* save current port status */
if (rte_eth_dev_save(devs, sizeof(devs)))
goto end;
/* walk around dev_driver_list to find the driver of the device,
* then invoke probe function o the driver.
* TODO:
* rte_eal_vdev_init() should return port_id,
* And rte_eth_dev_save() and rte_eth_dev_get_changed_port()
* should be removed. */
if (rte_eal_vdev_init(name, args))
goto end;
/* get port_id enabled by above procedures */
if (rte_eth_dev_get_changed_port(devs, &new_port_id))
goto end;
ret = 0;
*port_id = new_port_id;
end:
if (name)
free(name);
if (args)
free(args);
if (ret < 0)
RTE_LOG(ERR, EAL, "Driver, cannot attach the device\n");
return ret;
}
/* detach the new virtual device, then store the name of the device */
static int
rte_eth_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_is_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_uninit(name))
goto err;
strncpy(vdevname, name, sizeof(name));
return 0;
err:
RTE_LOG(ERR, EAL, "Driver, cannot detach the device\n");
return -1;
}
/* attach the new device, then store port_id of the device */
int
rte_eth_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_eth_dev_attach_pdev(&addr, port_id);
else
return rte_eth_dev_attach_vdev(devargs, port_id);
}
/* detach the device, then store the name of the device */
int
rte_eth_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_PCI) {
ret = rte_eth_dev_get_addr_by_port(port_id, &addr);
if (ret < 0)
return ret;
ret = rte_eth_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_eth_dev_detach_vdev(port_id, name);
}
#else /* RTE_LIBRTE_EAL_HOTPLUG */
int
rte_eth_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_eth_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 /* RTE_LIBRTE_EAL_HOTPLUG */
static int
rte_eth_dev_rx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues)
{

View File

@ -175,6 +175,8 @@ extern "C" {
#include <rte_log.h>
#include <rte_interrupts.h>
#include <rte_pci.h>
#include <rte_dev.h>
#include <rte_devargs.h>
#include <rte_mbuf.h>
#include "rte_ether.h"
#include "rte_eth_ctrl.h"
@ -1539,6 +1541,16 @@ extern struct rte_eth_dev rte_eth_devices[];
*/
extern uint8_t rte_eth_dev_count(void);
/**
* Function for internal use by port hotplug functions.
* Returns a ethdev slot specified by the unique identifier name.
* @param name
* The pointer to the Unique identifier name for each Ethernet device
* @return
* - The pointer to the ethdev slot, on success. NULL on error
*/
extern struct rte_eth_dev *rte_eth_dev_allocated(const char *name);
/**
* Function for internal use by dummy drivers primarily, e.g. ring-based
* driver.
@ -1565,6 +1577,32 @@ struct rte_eth_dev *rte_eth_dev_allocate(const char *name,
*/
int rte_eth_dev_release_port(struct rte_eth_dev *eth_dev);
/**
* Attach a new Ethernet device specified by aruguments.
*
* @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_eth_dev_attach(const char *devargs, uint8_t *port_id);
/**
* Detach a Ethernet device specified by port identifier.
*
* @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_eth_dev_detach(uint8_t port_id, char *devname);
struct eth_driver;
/**
* @internal

View File

@ -8,6 +8,8 @@ DPDK_2.0 {
rte_eth_allmulticast_enable;
rte_eth_allmulticast_get;
rte_eth_dev_allocate;
rte_eth_dev_allocated;
rte_eth_dev_attach;
rte_eth_dev_bypass_event_show;
rte_eth_dev_bypass_event_store;
rte_eth_dev_bypass_init;
@ -22,6 +24,7 @@ DPDK_2.0 {
rte_eth_dev_close;
rte_eth_dev_configure;
rte_eth_dev_count;
rte_eth_dev_detach;
rte_eth_dev_fdir_add_perfect_filter;
rte_eth_dev_fdir_add_signature_filter;
rte_eth_dev_fdir_get_infos;