mirror of
https://github.com/openvswitch/ovs
synced 2025-09-04 16:25:17 +00:00
netdev-dpdk: support port representors
Dpdk port representors were introduced in dpdk versions 18.xx. Prior to port representors there was a one-to-one relationship between an rte device (e.g. PCI bus) and an eth device (referenced as dpdk port id in OVS). With port representors the relationship becomes one-to-many rte device to eth devices. For example in [3] there are two devices (representors) using the same PCI physical address 0000:08:00.0: "0000:08:00.0,representor=[3]" and "0000:08:00.0,representor=[5]". This commit handles the new one-to-many relationship. For example, when one of the device port representors in [3] is closed - the PCI bus cannot be detached until the other device port representor is closed as well. OVS remains backward compatible by supporting dpdk legacy PCI ports which do not include port representors. Dpdk port representors related commits are listed in [1]. Dpdk port representors documentation appears in [2]. A sample configuration which uses two representors ports (the output of "ovs-vsctl show" command) is shown in [3]. [1] e0cb96204b71 ("net/i40e: add support for representor ports") cf80ba6e2038 ("net/ixgbe: add support for representor ports") 26c08b979d26 ("net/mlx5: add port representor awareness") [2] https://doc.dpdk.org/guides-18.11/prog_guide/switch_representation.html [3] Bridge "ovs_br0" Port "ovs_br0" Interface "ovs_br0" type: internal Port "port-rep3" Interface "port-rep3" type: dpdk options: {dpdk-devargs="0000:08:00.0,representor=[3]"} Port "port-rep5" Interface "port-rep5" type: dpdk options: {dpdk-devargs="0000:08:00.0,representor=[5]"} ovs_version: "2.10.90" Signed-off-by: Ophir Munk <ophirmu@mellanox.com> Co-authored-by: Ilya Maximets <i.maximets@samsung.com> Signed-off-by: Ilya Maximets <i.maximets@samsung.com> Signed-off-by: Ian Stokes <ian.stokes@intel.com>
This commit is contained in:
1
NEWS
1
NEWS
@@ -28,6 +28,7 @@ Post-v2.10.0
|
|||||||
It can be set with pmd-rxq-assign.
|
It can be set with pmd-rxq-assign.
|
||||||
* Add support for DPDK 18.11
|
* Add support for DPDK 18.11
|
||||||
* Add support for Auto load balancing of PMDs (experimental)
|
* Add support for Auto load balancing of PMDs (experimental)
|
||||||
|
* Add support for port representors.
|
||||||
- Add 'symmetric_l3' hash function.
|
- Add 'symmetric_l3' hash function.
|
||||||
- OVS now honors 'updelay' and 'downdelay' for bonds with LACP configured.
|
- OVS now honors 'updelay' and 'downdelay' for bonds with LACP configured.
|
||||||
- ovs-vswitchd:
|
- ovs-vswitchd:
|
||||||
|
@@ -1218,6 +1218,26 @@ dpdk_dev_parse_name(const char dev_name[], const char prefix[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the number of OVS interfaces which have the same DPDK
|
||||||
|
* rte device (e.g. same pci bus address).
|
||||||
|
* FIXME: avoid direct access to DPDK internal array rte_eth_devices.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
netdev_dpdk_get_num_ports(struct rte_device *device)
|
||||||
|
OVS_REQUIRES(dpdk_mutex)
|
||||||
|
{
|
||||||
|
struct netdev_dpdk *dev;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
LIST_FOR_EACH (dev, list_node, &dpdk_list) {
|
||||||
|
if (rte_eth_devices[dev->port_id].device == device
|
||||||
|
&& rte_eth_devices[dev->port_id].state != RTE_ETH_DEV_UNUSED) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vhost_common_construct(struct netdev *netdev)
|
vhost_common_construct(struct netdev *netdev)
|
||||||
OVS_REQUIRES(dpdk_mutex)
|
OVS_REQUIRES(dpdk_mutex)
|
||||||
@@ -1353,7 +1373,9 @@ static void
|
|||||||
netdev_dpdk_destruct(struct netdev *netdev)
|
netdev_dpdk_destruct(struct netdev *netdev)
|
||||||
{
|
{
|
||||||
struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
|
struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
|
||||||
struct rte_eth_dev_info dev_info;
|
struct rte_device *rte_dev;
|
||||||
|
struct rte_eth_dev *eth_dev;
|
||||||
|
bool remove_on_close;
|
||||||
|
|
||||||
ovs_mutex_lock(&dpdk_mutex);
|
ovs_mutex_lock(&dpdk_mutex);
|
||||||
|
|
||||||
@@ -1361,12 +1383,34 @@ netdev_dpdk_destruct(struct netdev *netdev)
|
|||||||
dev->started = false;
|
dev->started = false;
|
||||||
|
|
||||||
if (dev->attached) {
|
if (dev->attached) {
|
||||||
|
/* Retrieve eth device data before closing it.
|
||||||
|
* FIXME: avoid direct access to DPDK internal array rte_eth_devices.
|
||||||
|
*/
|
||||||
|
eth_dev = &rte_eth_devices[dev->port_id];
|
||||||
|
remove_on_close =
|
||||||
|
eth_dev->data &&
|
||||||
|
(eth_dev->data->dev_flags & RTE_ETH_DEV_CLOSE_REMOVE);
|
||||||
|
rte_dev = eth_dev->device;
|
||||||
|
|
||||||
|
/* Remove the eth device. */
|
||||||
rte_eth_dev_close(dev->port_id);
|
rte_eth_dev_close(dev->port_id);
|
||||||
rte_eth_dev_info_get(dev->port_id, &dev_info);
|
|
||||||
if (dev_info.device && !rte_dev_remove(dev_info.device)) {
|
/* Remove this rte device and all its eth devices if flag
|
||||||
VLOG_INFO("Device '%s' has been detached", dev->devargs);
|
* RTE_ETH_DEV_CLOSE_REMOVE is not supported (which means representors
|
||||||
|
* are not supported), or if all the eth devices belonging to the rte
|
||||||
|
* device are closed.
|
||||||
|
*/
|
||||||
|
if (!remove_on_close || !netdev_dpdk_get_num_ports(rte_dev)) {
|
||||||
|
if (rte_dev_remove(rte_dev) < 0) {
|
||||||
|
VLOG_ERR("Device '%s' can not be detached", dev->devargs);
|
||||||
|
} else {
|
||||||
|
/* Device was closed and detached. */
|
||||||
|
VLOG_INFO("Device '%s' has been removed and detached",
|
||||||
|
dev->devargs);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
VLOG_ERR("Device '%s' can not be detached", dev->devargs);
|
/* Device was only closed. rte_dev_remove() was not called. */
|
||||||
|
VLOG_INFO("Device '%s' has been removed", dev->devargs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1632,8 +1676,25 @@ netdev_dpdk_get_port_by_mac(const char *mac_str)
|
|||||||
return DPDK_ETH_PORT_ID_INVALID;
|
return DPDK_ETH_PORT_ID_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the first DPDK port id matching the devargs pattern. */
|
||||||
|
static dpdk_port_t netdev_dpdk_get_port_by_devargs(const char *devargs)
|
||||||
|
OVS_REQUIRES(dpdk_mutex)
|
||||||
|
{
|
||||||
|
dpdk_port_t port_id;
|
||||||
|
struct rte_dev_iterator iterator;
|
||||||
|
|
||||||
|
RTE_ETH_FOREACH_MATCHING_DEV (port_id, devargs, &iterator) {
|
||||||
|
/* If a break is done - must call rte_eth_iterator_cleanup. */
|
||||||
|
rte_eth_iterator_cleanup(&iterator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return port_id;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Normally, a PCI id is enough for identifying a specific DPDK port.
|
* Normally, a PCI id (optionally followed by a representor number)
|
||||||
|
* is enough for identifying a specific DPDK port.
|
||||||
* However, for some NICs having multiple ports sharing the same PCI
|
* However, for some NICs having multiple ports sharing the same PCI
|
||||||
* id, using PCI id won't work then.
|
* id, using PCI id won't work then.
|
||||||
*
|
*
|
||||||
@@ -1645,28 +1706,30 @@ netdev_dpdk_get_port_by_mac(const char *mac_str)
|
|||||||
static dpdk_port_t
|
static dpdk_port_t
|
||||||
netdev_dpdk_process_devargs(struct netdev_dpdk *dev,
|
netdev_dpdk_process_devargs(struct netdev_dpdk *dev,
|
||||||
const char *devargs, char **errp)
|
const char *devargs, char **errp)
|
||||||
|
OVS_REQUIRES(dpdk_mutex)
|
||||||
{
|
{
|
||||||
char *name;
|
dpdk_port_t new_port_id;
|
||||||
dpdk_port_t new_port_id = DPDK_ETH_PORT_ID_INVALID;
|
|
||||||
|
|
||||||
if (strncmp(devargs, "class=eth,mac=", 14) == 0) {
|
if (strncmp(devargs, "class=eth,mac=", 14) == 0) {
|
||||||
new_port_id = netdev_dpdk_get_port_by_mac(&devargs[14]);
|
new_port_id = netdev_dpdk_get_port_by_mac(&devargs[14]);
|
||||||
} else {
|
} else {
|
||||||
name = xmemdup0(devargs, strcspn(devargs, ","));
|
new_port_id = netdev_dpdk_get_port_by_devargs(devargs);
|
||||||
if (rte_eth_dev_get_port_by_name(name, &new_port_id)
|
if (!rte_eth_dev_is_valid_port(new_port_id)) {
|
||||||
|| !rte_eth_dev_is_valid_port(new_port_id)) {
|
|
||||||
/* Device not found in DPDK, attempt to attach it */
|
/* Device not found in DPDK, attempt to attach it */
|
||||||
if (!rte_dev_probe(devargs)
|
if (rte_dev_probe(devargs)) {
|
||||||
&& !rte_eth_dev_get_port_by_name(name, &new_port_id)) {
|
|
||||||
/* Attach successful */
|
|
||||||
dev->attached = true;
|
|
||||||
VLOG_INFO("Device '%s' attached to DPDK", devargs);
|
|
||||||
} else {
|
|
||||||
/* Attach unsuccessful */
|
|
||||||
new_port_id = DPDK_ETH_PORT_ID_INVALID;
|
new_port_id = DPDK_ETH_PORT_ID_INVALID;
|
||||||
|
} else {
|
||||||
|
new_port_id = netdev_dpdk_get_port_by_devargs(devargs);
|
||||||
|
if (rte_eth_dev_is_valid_port(new_port_id)) {
|
||||||
|
/* Attach successful */
|
||||||
|
dev->attached = true;
|
||||||
|
VLOG_INFO("Device '%s' attached to DPDK", devargs);
|
||||||
|
} else {
|
||||||
|
/* Attach unsuccessful */
|
||||||
|
new_port_id = DPDK_ETH_PORT_ID_INVALID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_port_id == DPDK_ETH_PORT_ID_INVALID) {
|
if (new_port_id == DPDK_ETH_PORT_ID_INVALID) {
|
||||||
@@ -1761,7 +1824,7 @@ netdev_dpdk_set_config(struct netdev *netdev, const struct smap *args,
|
|||||||
dup_dev = netdev_dpdk_lookup_by_port_id(new_port_id);
|
dup_dev = netdev_dpdk_lookup_by_port_id(new_port_id);
|
||||||
if (dup_dev) {
|
if (dup_dev) {
|
||||||
VLOG_WARN_BUF(errp, "'%s' is trying to use device '%s' "
|
VLOG_WARN_BUF(errp, "'%s' is trying to use device '%s' "
|
||||||
"which is already in use by '%s'",
|
"which is already in use by '%s'",
|
||||||
netdev_get_name(netdev), new_devargs,
|
netdev_get_name(netdev), new_devargs,
|
||||||
netdev_get_name(&dup_dev->up));
|
netdev_get_name(&dup_dev->up));
|
||||||
err = EADDRINUSE;
|
err = EADDRINUSE;
|
||||||
@@ -3236,32 +3299,49 @@ netdev_dpdk_detach(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
|||||||
char *response;
|
char *response;
|
||||||
dpdk_port_t port_id;
|
dpdk_port_t port_id;
|
||||||
struct netdev_dpdk *dev;
|
struct netdev_dpdk *dev;
|
||||||
struct rte_eth_dev_info dev_info;
|
struct rte_device *rte_dev;
|
||||||
|
struct ds used_interfaces = DS_EMPTY_INITIALIZER;
|
||||||
|
bool used = false;
|
||||||
|
|
||||||
ovs_mutex_lock(&dpdk_mutex);
|
ovs_mutex_lock(&dpdk_mutex);
|
||||||
|
|
||||||
if (rte_eth_dev_get_port_by_name(argv[1], &port_id)) {
|
port_id = netdev_dpdk_get_port_by_devargs(argv[1]);
|
||||||
|
if (!rte_eth_dev_is_valid_port(port_id)) {
|
||||||
response = xasprintf("Device '%s' not found in DPDK", argv[1]);
|
response = xasprintf("Device '%s' not found in DPDK", argv[1]);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = netdev_dpdk_lookup_by_port_id(port_id);
|
rte_dev = rte_eth_devices[port_id].device;
|
||||||
if (dev) {
|
ds_put_format(&used_interfaces,
|
||||||
response = xasprintf("Device '%s' is being used by interface '%s'. "
|
"Device '%s' is being used by the following interfaces:",
|
||||||
"Remove it before detaching",
|
argv[1]);
|
||||||
argv[1], netdev_get_name(&dev->up));
|
|
||||||
goto error;
|
LIST_FOR_EACH (dev, list_node, &dpdk_list) {
|
||||||
|
/* FIXME: avoid direct access to DPDK array rte_eth_devices. */
|
||||||
|
if (rte_eth_devices[dev->port_id].device == rte_dev
|
||||||
|
&& rte_eth_devices[dev->port_id].state != RTE_ETH_DEV_UNUSED) {
|
||||||
|
used = true;
|
||||||
|
ds_put_format(&used_interfaces, " %s",
|
||||||
|
netdev_get_name(&dev->up));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rte_eth_dev_close(port_id);
|
if (used) {
|
||||||
|
ds_put_cstr(&used_interfaces, ". Remove them before detaching.");
|
||||||
|
response = ds_steal_cstr(&used_interfaces);
|
||||||
|
ds_destroy(&used_interfaces);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
ds_destroy(&used_interfaces);
|
||||||
|
|
||||||
rte_eth_dev_info_get(port_id, &dev_info);
|
rte_eth_dev_close(port_id);
|
||||||
if (!dev_info.device || rte_dev_remove(dev_info.device)) {
|
if (rte_dev_remove(rte_dev) < 0) {
|
||||||
response = xasprintf("Device '%s' can not be detached", argv[1]);
|
response = xasprintf("Device '%s' can not be detached", argv[1]);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
response = xasprintf("Device '%s' has been detached", argv[1]);
|
response = xasprintf("All devices shared with device '%s' "
|
||||||
|
"have been detached", argv[1]);
|
||||||
|
|
||||||
ovs_mutex_unlock(&dpdk_mutex);
|
ovs_mutex_unlock(&dpdk_mutex);
|
||||||
unixctl_command_reply(conn, response);
|
unixctl_command_reply(conn, response);
|
||||||
|
Reference in New Issue
Block a user