@@ -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);