[ovs-dev,net-next,4/6] netdev-dpdk: Add dpdk-bond support

Message ID 1523537572-2595-5-git-send-email-xiangxia.m.yue@gmail.com
State Rejected
Delegated to: Ian Stokes
Headers show
Series
  • Add dpdk-bond support
Related show

Commit Message

Tonghao Zhang April 12, 2018, 12:52 p.m.
From: Tonghao Zhang <xiangxia.m.yue@gmail.com>

This patch implements, mostly the dpdk-bond support.
vswitchd try to parse devargs as dpdk-bond device.
If success, create a bond device and add slave ports
to it. And the bond device id will be set to dev->port_id
as a normal interface.

    * check pci whether it's valid or not. On success, next step.
    * check whether the dpdk-bond was created.
    * create a new bond device and add slave ports to it.
    * update the netdev_dpdk_bond struct.

By default, the mode of dpdk-bond device is active-backup.

Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
---
 lib/netdev-dpdk.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 160 insertions(+), 2 deletions(-)

Patch

diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index 8cba466..e1285d1 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -143,6 +143,12 @@  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
 #define DPDK_ETH_PORT_ID_INVALID    RTE_MAX_ETHPORTS
 #define DPDK_BOND_SLAVE_MAX         RTE_MAX_ETHPORTS
+#define DPDK_BOND_NAME_MAX          (RTE_MAX_ETHPORTS + 10)
+#define DPDK_BOND_NAME_FMT          "eth_bond%s"
+
+#define LIST_EACH_VALID_BOND_SLAVE(SLAVES) \
+    for (int i = 0; i < DPDK_BOND_SLAVE_MAX && \
+            SLAVES[i] != DPDK_ETH_PORT_ID_INVALID; i++)
 
 /* DPDK library uses uint16_t for port_id. */
 typedef uint16_t dpdk_port_t;
@@ -901,6 +907,7 @@  netdev_dpdk_alloc_txq(unsigned int n_txqs)
 
 static void netdev_dpdk_bond_init(struct netdev_dpdk *dev);
 static void netdev_dpdk_bond_uninit(struct netdev_dpdk *dev);
+static void netdev_dpdk_bond_slave_del(dpdk_port_t bp, dpdk_port_t *rs);
 
 static int
 common_construct(struct netdev *netdev, dpdk_port_t port_no,
@@ -1396,20 +1403,171 @@  netdev_dpdk_get_port_by_mac(const char *mac_str)
 }
 
 static void
+netdev_dpdk_bond_slave_init(dpdk_port_t *s)
+{
+    for (int i = 0; i < DPDK_BOND_SLAVE_MAX; i++) {
+        s[i] = DPDK_ETH_PORT_ID_INVALID;
+    }
+}
+
+static void
 netdev_dpdk_bond_init(struct netdev_dpdk *dev)
 {
     dev->bond = xmalloc(sizeof *dev->bond);
     dev->bond->name = NULL;
     dev->bond->mode = MODE_ACTIVE_BACKUP;
+
+    netdev_dpdk_bond_slave_init(dev->bond->slaves);
 }
 
 static void
 netdev_dpdk_bond_uninit(struct netdev_dpdk *dev)
 {
+    /* The interface may be created as bond device.
+     * Try to release it as a bond device. */
+    netdev_dpdk_bond_slave_del(dev->port_id, dev->bond->slaves);
+    rte_eth_bond_free(dev->bond->name);
+
     free(dev->bond->name);
     free(dev->bond);
 }
 
+/* Check the PCIs or device names requested whether
+ * it's valid or not. */
+static bool
+netdev_dpdk_bond_slave_request(const struct netdev_dpdk *dev OVS_UNUSED,
+                               const char *devargs, dpdk_port_t *rs)
+{
+    dpdk_port_t port_id = DPDK_ETH_PORT_ID_INVALID;
+    char *pci = NULL, *save_ptr = NULL;
+    char *args = xstrdup(devargs);
+    int i;
+
+    netdev_dpdk_bond_slave_init(rs);
+
+    for (pci = strtok_r(args, ",", &save_ptr), i = 0;
+            pci != NULL; pci = strtok_r(NULL, ",", &save_ptr), i++) {
+
+        if (rte_eth_dev_get_port_by_name(pci, &port_id) ||
+                !rte_eth_dev_is_valid_port(port_id)) {
+
+            VLOG_INFO("Could not get the port id from pci address "
+                      "or device name: %s, or the device is not attached, "
+                      "or has been used by openvswitch.\n", pci);
+            free(args);
+            return false;
+        }
+
+        rs[i] = port_id;
+    }
+
+    free(args);
+    return true;
+}
+
+static bool
+netdev_dpdk_bond_slave_eq(dpdk_port_t *a, dpdk_port_t *b)
+{
+    return memcmp(a, b, DPDK_BOND_SLAVE_MAX * sizeof(*a)) == 0;
+}
+
+static void
+netdev_dpdk_bond_update(struct netdev_dpdk *dev,
+                        char *namebuf, dpdk_port_t *rs)
+{
+    memcpy(dev->bond->slaves, rs,
+           DPDK_BOND_SLAVE_MAX * sizeof(*rs));
+
+    free(dev->bond->name);
+    dev->bond->name = xstrdup(namebuf);
+}
+
+static void
+netdev_dpdk_bond_slave_del(dpdk_port_t bp, dpdk_port_t *rs)
+{
+    LIST_EACH_VALID_BOND_SLAVE(rs) {
+        rte_eth_bond_slave_remove(bp, rs[i]);
+    }
+}
+
+static int
+netdev_dpdk_bond_slave_add(dpdk_port_t bp, dpdk_port_t *rs)
+{
+    LIST_EACH_VALID_BOND_SLAVE(rs) {
+        if (rte_eth_bond_slave_add(bp, rs[i])) {
+            goto out;
+        }
+    }
+
+    return true;
+
+out:
+    netdev_dpdk_bond_slave_del(bp, rs);
+    return false;
+}
+
+static char *
+netdev_dpdk_bond_name_key(char *key, dpdk_port_t *rs)
+{
+    LIST_EACH_VALID_BOND_SLAVE(rs) {
+        key[i] = '0' + rs[i];
+    }
+
+    return key;
+}
+
+/* Try to parse devargs as dpdk-bond device. If success,
+ * create a bond device and add slave ports to it. And the
+ * bond devcie id will be set to dev->port_id as a normal
+ * interface. */
+static dpdk_port_t
+netdev_dpdk_bond_devargs(struct netdev_dpdk *dev, const char *devargs)
+{
+    dpdk_port_t request_slaves[DPDK_BOND_SLAVE_MAX];
+    char namebuf[DPDK_BOND_NAME_MAX];
+    char namekey[DPDK_BOND_NAME_MAX];
+    int bond_port;
+
+    if (!netdev_dpdk_bond_slave_request(dev, devargs, request_slaves)) {
+        return DPDK_ETH_PORT_ID_INVALID;
+    }
+
+    if (netdev_dpdk_bond_slave_eq(dev->bond->slaves, request_slaves)) {
+        /* Already created, return its id directly. */
+        return dev->port_id;
+    }
+
+    snprintf(namebuf, sizeof(namebuf) - 1,
+             DPDK_BOND_NAME_FMT,
+             netdev_dpdk_bond_name_key(namekey, request_slaves));
+
+    bond_port = rte_eth_bond_create(namebuf,
+                                    dev->bond->mode,
+                                    rte_socket_id());
+
+    if (bond_port < 0) {
+        VLOG_INFO("Try to new a bond device with name: %s, but failed.\n",
+                  namebuf);
+        return DPDK_ETH_PORT_ID_INVALID;
+    }
+
+    if (!netdev_dpdk_bond_slave_add(bond_port, request_slaves)) {
+        VLOG_INFO("Add slave ports to new bond device, but failed.\n");
+
+        rte_eth_bond_free(namebuf);
+        return DPDK_ETH_PORT_ID_INVALID;
+    }
+
+    /* Free the old slave ports and master port. */
+    netdev_dpdk_bond_slave_del(dev->port_id, dev->bond->slaves);
+    rte_eth_bond_free(dev->bond->name);
+
+    netdev_dpdk_bond_update(dev, namebuf, request_slaves);
+
+    /* Do not update: dev->port_id = bond_port. */
+    return bond_port;
+}
+
 /*
  * Normally, a PCI id is enough for identifying a specific DPDK port.
  * However, for some NICs having multiple ports sharing the same PCI
@@ -1439,8 +1597,8 @@  netdev_dpdk_process_devargs(struct netdev_dpdk *dev,
                 dev->attached = true;
                 VLOG_INFO("Device '%s' attached to DPDK", devargs);
             } else {
-                /* Attach unsuccessful */
-                new_port_id = DPDK_ETH_PORT_ID_INVALID;
+                /* Try to parse it as dpdk-bond args. */
+                new_port_id = netdev_dpdk_bond_devargs(dev, devargs);
             }
         }
         free(name);