* [dpdk-dev] [PATCH 0/4] Link Bonding Library @ 2014-05-28 15:32 declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 1/4] " declan.doherty ` (7 more replies) 0 siblings, 8 replies; 127+ messages in thread From: declan.doherty @ 2014-05-28 15:32 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Initial release of Link Bonding Library (lib/librte_bond) with support for bonding modes : 0 - Round Robin 1 - Active Backup 2 - Balance l2 / l23 / l34 3 - Broadcast patches split: 1 - library + makefile changes 2 - Unit test suite, including code to generate packet bursts for testing rx and tx functionality of bonded device and a virtual/stubbed out ethdev for use as slave ethdev in testing 3 - Link bonding integration into testpmd, including : - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices 4 - Add Link Bonding Library to Doxygen app/test-pmd/cmdline.c | 550 +++++ app/test-pmd/parameters.c | 4 +- app/test-pmd/testpmd.c | 28 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 3 + app/test/commands.c | 3 + app/test/packet_burst_generator.c | 276 +++ app/test/packet_burst_generator.h | 85 + app/test/test.h | 1 + app/test/test_link_bonding.c | 4007 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 580 ++++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_bond/Makefile | 28 + lib/librte_bond/rte_bond.c | 1679 ++++++++++++++++ lib/librte_bond/rte_bond.h | 228 +++ mk/rte.app.mk | 5 + 21 files changed, 7564 insertions(+), 2 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c create mode 100644 lib/librte_bond/rte_bond.h -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH 1/4] Link Bonding Library 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty @ 2014-05-28 15:32 ` declan.doherty 2014-05-28 16:54 ` Shaw, Jeffrey B 2014-05-28 15:32 ` [dpdk-dev] [PATCH 2/4] Link bonding unit tests declan.doherty ` (6 subsequent siblings) 7 siblings, 1 reply; 127+ messages in thread From: declan.doherty @ 2014-05-28 15:32 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Link Bonding Library (lib/librte_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_bond/Makefile | 28 + lib/librte_bond/rte_bond.c | 1679 ++++++++++++++++++++++++++++++++++++++++++++ lib/librte_bond/rte_bond.h | 228 ++++++ mk/rte.app.mk | 5 + 7 files changed, 1951 insertions(+) create mode 100644 lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c create mode 100644 lib/librte_bond/rte_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 2cc7b80..53ed8b9 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -187,6 +187,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding library +# +CONFIG_RTE_LIBRTE_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 62619c6..35b525a 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -211,6 +211,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n +# +# Compile link bonding library +# +CONFIG_RTE_LIBRTE_BOND=y + CONFIG_RTE_LIBRTE_PMD_XENVIRT=n # diff --git a/lib/Makefile b/lib/Makefile index b92b392..9995ba8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -47,6 +47,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_BOND) += librte_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_NET) += librte_net diff --git a/lib/librte_bond/Makefile b/lib/librte_bond/Makefile new file mode 100644 index 0000000..7514378 --- /dev/null +++ b/lib/librte_bond/Makefile @@ -0,0 +1,28 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_BOND) += rte_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_bond.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_BOND) += lib/librte_mbuf lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_BOND) += lib/librte_malloc + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_bond/rte_bond.c b/lib/librte_bond/rte_bond.c new file mode 100644 index 0000000..35dff25 --- /dev/null +++ b/lib/librte_bond/rte_bond.c @@ -0,0 +1,1679 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_cycles.h> +#include <rte_ip.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_bond.h" + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; /**< Queue Id */ + struct bond_dev_private *dev_private; /**< Reference to eth_dev private + structure */ + + uint16_t nb_rx_desc; /**< Number of RX descriptors + available for the queue */ + struct rte_eth_rxconf rx_conf; /**< Copy of RX configuration + structure for queue */ + struct rte_mempool *mb_pool; /**< Reference to mbuf pool to use + for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; /**< Queue Id */ + struct bond_dev_private *dev_private; /**< Reference to dev private + structure */ + uint16_t nb_tx_desc; /**< Number of TX descriptors + available for the queue */ + struct rte_eth_txconf tx_conf; /**< Copy of TX configuration + structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; /**< Slave eth_dev original MAC address */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t balance_xmit_policy; /**< Transmit policy - l2 / l23 / l34 + for operation in balance mode */ + uint8_t user_defined_mac; /**< Flag for whether MAC address is + user defined or not */ + uint8_t promiscuous_en; /**< Enabled/disable promiscuous mode on + slave devices */ + uint8_t link_props_set; /**< Bonded eth_dev link properties set*/ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int last_slave_idx = -1; + int i, slave_idx; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + slave_idx = i % num_of_slaves; + slave_bufs[slave_idx][(slave_nb_pkts[slave_idx])++] = bufs[i]; + } + + /* calculate the next slave to transmit on based on the last slave idx used + * in the last call to bond_ethdev_tx_burst_round_robin */ + slave_idx = last_slave_idx + 1; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + slave_idx = (slave_idx + i) % num_of_slaves; + + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[slave_idx], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + } + } + + last_slave_idx = slave_idx; + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr) & 0xFFFF; +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) { + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_broadcast(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + + for (i = 0; i < num_of_slaves; i++) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + } + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr))) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals; + int i; + + internals = bonded_eth_dev->data->dev_private; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf; + struct bond_dev_private *internals; + + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + internals = bonded_eth_dev->data->dev_private; + + presisted_slave_conf = slave_config_get(internals, + slave_eth_dev->data->port_id); + + if (presisted_slave_conf == NULL) + return -1; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), + slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) " + "of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (!internals->user_defined_mac) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &conf->mac_addr) != 0) + return -1; + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q; + + bd_rx_q = (struct bond_rx_queue *)rte_zmalloc_socket(NULL, + sizeof(struct bond_rx_queue), 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)rte_zmalloc_socket(NULL, + sizeof(struct bond_tx_queue), 0, dev->pci_dev->numa_node); + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->primary_port) { + if (internals->active_slave_count > 0) + internals->primary_port = internals->active_slaves[0]; + else + internals->primary_port = internals->slaves[0]; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + + + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count == 0) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + rte_eth_bond_primary_set(bonded_port_id, slave_port_id); + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + /* UnRegister link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < (internals->slave_count - 1)) + internals->slaves[i] = internals->slaves[i+1]; + } + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + if (pos < 0) + goto err_del; + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->primary_port == slave_port_id && + internals->active_slave_count > 0) + internals->primary_port = internals->active_slaves[0]; + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + int i; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) { + /* Found slave device in active slave list */ + internals->primary_port = slave_port_id; + return 0; + } + } + + /* Slave is not bound to this master device */ + return -1; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->primary_port; +} + +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + *slaves = (uint8_t *)(&internals->slaves); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + *slaves = (uint8_t *)(&internals->active_slaves); + + return internals->active_slave_count; + +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_bond/rte_bond.h b/lib/librte_bond/rte_bond.h new file mode 100644 index 0000000..97b6d5e --- /dev/null +++ b/lib/librte_bond/rte_bond.h @@ -0,0 +1,228 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/** Link Bonding Mode Definitions */ +#define BONDING_MODE_ROUND_ROBIN (0) +#define BONDING_MODE_ACTIVE_BACKUP (1) +#define BONDING_MODE_BALANCE (2) +#define BONDING_MODE_BROADCAST (3) + +/** Balance Mode Transmit Policy Types */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +#define BALANCE_XMIT_POLICY_LAYER23 (1) +#define BALANCE_XMIT_POLICY_LAYER34 (2) + +/** + * Create a bonded rte_eth_dev device + * + * @param name + * @param mode + * @param socket_id + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id + * @param mode + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id + * @param slaves + * + * @return + * number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id + * @param slaves + * + * @return + * number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id + * @param mac_addr + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode + * + * @param bonded_port_id + * @param policy + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id + * + * @return + * balance transmit policy on success, negative value otherwise + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index a836577..a803a5c 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -177,8 +177,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_BOND),y) +LDLIBS += -lrte_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH 1/4] Link Bonding Library 2014-05-28 15:32 ` [dpdk-dev] [PATCH 1/4] " declan.doherty @ 2014-05-28 16:54 ` Shaw, Jeffrey B 2014-05-29 13:32 ` Doherty, Declan 0 siblings, 1 reply; 127+ messages in thread From: Shaw, Jeffrey B @ 2014-05-28 16:54 UTC (permalink / raw) To: Doherty, Declan, dev Hi Declan, I'm worried about one thing in "bond_ethdev_tx_broadcast()" related to freeing of the broadcasted packets. > +static uint16_t > +bond_ethdev_tx_broadcast(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) > +{ > + struct bond_dev_private *internals; > + struct bond_tx_queue *bd_tx_q; > + > + uint8_t num_of_slaves; > + uint8_t slaves[RTE_MAX_ETHPORTS]; > + > + uint16_t num_tx_total = 0; > + > + int i; > + > + bd_tx_q = (struct bond_tx_queue *)queue; > + internals = bd_tx_q->dev_private; > + > + /* Copy slave list to protect against slave up/down changes during tx > + * bursting */ > + num_of_slaves = internals->active_slave_count; > + memcpy(slaves, internals->active_slaves, > + sizeof(internals->active_slaves[0]) * num_of_slaves); > + > + if (num_of_slaves < 1) > + return 0; > + > + > + for (i = 0; i < num_of_slaves; i++) { > + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, > + bufs, nb_pkts); > + } > + > + return num_tx_total; > +} > + Transmitting the same buffers on all slaves will cause problems when the PMD frees the mbufs for previously transmitted packets. So if you broadcast 1 packet on 4 slaves, each of the 4 slaves will (eventually) have to free the same mbuf. Without updating the refcnt, you will end up incorrectly freeing the same mbuf 3 extra times. Your test application does not catch this case since the max packets that are tested is 320, which is less than the size of the Tx descriptor rings (512). Try testing with more packets and you should see some latent segmentation faults. You should also see this with testpmd, if you try broadcasting enough packets. Thanks, Jeff ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH 1/4] Link Bonding Library 2014-05-28 16:54 ` Shaw, Jeffrey B @ 2014-05-29 13:32 ` Doherty, Declan 0 siblings, 0 replies; 127+ messages in thread From: Doherty, Declan @ 2014-05-29 13:32 UTC (permalink / raw) To: Shaw, Jeffrey B, dev > -----Original Message----- > From: Shaw, Jeffrey B > Sent: Wednesday, May 28, 2014 5:55 PM > To: Doherty, Declan; dev@dpdk.org > Subject: RE: [dpdk-dev] [PATCH 1/4] Link Bonding Library > > Hi Declan, > I'm worried about one thing in "bond_ethdev_tx_broadcast()" related to > freeing of the broadcasted packets. > > > +static uint16_t > > +bond_ethdev_tx_broadcast(void *queue, struct rte_mbuf **bufs, > > +uint16_t nb_pkts) { > > + struct bond_dev_private *internals; > > + struct bond_tx_queue *bd_tx_q; > > + > > + uint8_t num_of_slaves; > > + uint8_t slaves[RTE_MAX_ETHPORTS]; > > + > > + uint16_t num_tx_total = 0; > > + > > + int i; > > + > > + bd_tx_q = (struct bond_tx_queue *)queue; > > + internals = bd_tx_q->dev_private; > > + > > + /* Copy slave list to protect against slave up/down changes during tx > > + * bursting */ > > + num_of_slaves = internals->active_slave_count; > > + memcpy(slaves, internals->active_slaves, > > + sizeof(internals->active_slaves[0]) * num_of_slaves); > > + > > + if (num_of_slaves < 1) > > + return 0; > > + > > + > > + for (i = 0; i < num_of_slaves; i++) { > > + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q- > >queue_id, > > + bufs, nb_pkts); > > + } > > + > > + return num_tx_total; > > +} > > + > > Transmitting the same buffers on all slaves will cause problems when the PMD > frees the mbufs for previously transmitted packets. > So if you broadcast 1 packet on 4 slaves, each of the 4 slaves will (eventually) > have to free the same mbuf. Without updating the refcnt, you will end up > incorrectly freeing the same mbuf 3 extra times. > > Your test application does not catch this case since the max packets that are > tested is 320, which is less than the size of the Tx descriptor rings (512). Try > testing with more packets and you should see some latent segmentation > faults. You should also see this with testpmd, if you try broadcasting enough > packets. > > > Thanks, > Jeff Hey Jeff, I'm not sure why I didn't see this in testing using on the test-pmd app, but it's obvious that this will cause issues. I'll add code to increment the refcnt in each mbuf by (num_of_slaves -1) in the bond_ethdev_tx_broadcast() function and also add a unit test to validate this in the next version of the patch. Thanks, Declan -------------------------------------------------------------- Intel Shannon Limited Registered in Ireland Registered Office: Collinstown Industrial Park, Leixlip, County Kildare Registered Number: 308263 Business address: Dromore House, East Park, Shannon, Co. Clare This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH 2/4] Link bonding unit tests 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 1/4] " declan.doherty @ 2014-05-28 15:32 ` declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 3/4] Link bonding integration into testpmd declan.doherty ` (5 subsequent siblings) 7 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-05-28 15:32 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Link bonding unit tests, including code to generate packet bursts for testing rx and tx functionality of bonded device and a virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 3 + app/test/commands.c | 3 + app/test/packet_burst_generator.c | 276 +++ app/test/packet_burst_generator.h | 85 + app/test/test.h | 1 + app/test/test_link_bonding.c | 4007 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 580 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 5029 insertions(+) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index b49785e..ac55a11 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -56,6 +56,7 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ring_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_rwlock.c SRCS-$(CONFIG_RTE_APP_TEST) += test_timer.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mbuf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_logs.c @@ -94,6 +95,8 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_common.c SRCS-$(CONFIG_RTE_APP_TEST) += test_timer_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c diff --git a/app/test/commands.c b/app/test/commands.c index efa8566..4d0ec3b 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -157,6 +157,8 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -221,6 +223,7 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" + "link_bonding_autotest#" "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..8838068 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,276 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char*) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr * vhdr = (struct vlan_hdr *)((uint8_t*)eth_hdr + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t*) ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + if (ip_cksum > 65535) + ip_cksum -= 65535; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, struct ether_hdr* eth_hdr, uint8_t vlan_enabled, void *ip_hdr, uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { + nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } + else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} + diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..6b97860 --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,85 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a,b,c,d)(((a & 0xff) << 24) | ((b & 0xff)<< 16) | ((c & 0xff) << 8) | (d & 0xff)) + + +void initialize_eth_header(struct ether_hdr *eth_hdr, + struct ether_addr *src_mac, + struct ether_addr *dst_mac, + uint8_t vlan_enabled, + uint16_t van_id); + +uint16_t initialize_udp_header(struct udp_hdr *udp_hdr, + uint16_t src_port, + uint16_t dst_port, + uint16_t pkt_data_len); + + +uint16_t initialize_ipv6_header(struct ipv6_hdr *ip_hdr, + uint8_t *src_addr, + uint8_t *dst_addr, + uint16_t pkt_data_len); + +uint16_t initialize_ipv4_header(struct ipv4_hdr *ip_hdr, + uint32_t src_addr, + uint32_t dst_addr, + uint16_t pkt_data_len); + +int generate_packet_burst(struct rte_mempool *mp, + struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, + uint8_t vlan_enabled, + void *ip_hdr, uint8_t ipv4, + struct udp_hdr *udp_hdr, + int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index 1945d29..7213e1a 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -94,6 +94,7 @@ int test_pmd_ring(void); int test_ivshmem(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..523a69b --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,4007 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192,168,1,98); +static uint32_t dst_addr_0 = IPV4_ADDR(192,168,1,98); +static uint32_t dst_addr_1 = IPV4_ADDR(193,166,1,99); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id= 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id ++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < + 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id ++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + test_params->slave_port_ids[i] = virtual_ethdev_create("slave_pmd_1", + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + const uint8_t *slaves; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + const uint8_t *slaves; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + return -1; + } + + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, &slaves); + if (current_slave_count >= 0) { + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + &slaves); + if (current_slave_count >= 0) { + return -1; + } + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL); + if (current_slave_count >= 0) { + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL); + if (current_slave_count >= 0) { + return -1; + } + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL); + if (current_slave_count >= 0) { + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL); + if (current_slave_count >= 0) { + return -1; + } + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) { + return -1; + } + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int retval, i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + return -1; + } + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) { + return -1; + } + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + const uint8_t *slaves; + + /* Add slave to bonded device*/ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + return -1; + + } + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active slave + * list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + const uint8_t *slaves; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value " + "(%d).\n", bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value " + "(%d)\n", test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) { + return -1; + } + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary " + "port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) { + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) " + "slaves.\n", test_params->bonded_port_id, bonding_mode, + number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count -1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) { + enable_bonded_slaves(); + } + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void * ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } + else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t*)src_ipv6_addr, (uint8_t*)dst_ipv6_addr_1, pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t*)src_ipv6_addr, (uint8_t*)dst_ipv6_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) { + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i <test_params-> bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) { + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected" + " (%d)\n", test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected" + " (%d)\n", test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] + [MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary " + "port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary " + "port\n",test_params-> slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary " + "port\n",test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1)!= 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT] + [MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + const uint8_t *slaves; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1)!= 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves) != TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + } + if (gen_pkt_burst[1][i] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + if (gen_pkt_burst[3][i] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + return -1; + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) { + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected " + "(%d)\n", test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected " + "(%d)\n", test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) { + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected " + "(%d)\n", test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as " + "expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as " + "expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected" + " (%d)\n", test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT] + [MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port not as expected"); + } + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) + != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) + != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) { + printf("Primary port not as expected"); + } + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) {; + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + // Invalid port id + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) { + return -1; + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] + [MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) { + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) { + return -1; + } + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) { + return -1; + } + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) { + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) { + return -1; + } + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) { + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) { + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1) ; +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) { + return -1; + } + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) { + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) { + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT] + [MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, 0) + != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1)!= 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1)!= 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT] + [MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1)!= 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) != + 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + return -1; + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value " + "(%u) not as expected (%d)\n", test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) { + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES] + [MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1)!= 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1)!= 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1)!= 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary " + "port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary " + "port\n", test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES] + [MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1)!= 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_reset(test_params->slave_port_ids[i]); + } + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) { + free(test_params->pkt_eth_hdr); + } + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char* success_msg; + const char* fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour " + "succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover " + "succeeded", + "test_activebackup_verify_slave_link_status_change_failover " + "failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) { + return -1; + } + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } + else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) { + return -1; + } + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..9dcaf44 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,580 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue*) + rte_zmalloc_socket(NULL, sizeof(struct virtual_ethdev_queue), + 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue*) + rte_zmalloc_socket(NULL, sizeof(struct virtual_ethdev_queue), 0, + socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) { + bonded_eth_dev->data->dev_link.link_status = 0; + } + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if(stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue*)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue*)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if(bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) { + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + } + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg * ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH 3/4] Link bonding integration into testpmd 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 1/4] " declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 2/4] Link bonding unit tests declan.doherty @ 2014-05-28 15:32 ` declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 4/4] Add Link Bonding Library to Doxygen declan.doherty ` (4 subsequent siblings) 7 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-05-28 15:32 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Adding link bonding support to testpmd. - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 550 ++++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/parameters.c | 4 +- app/test-pmd/testpmd.c | 28 ++- app/test-pmd/testpmd.h | 2 + 4 files changed, 582 insertions(+), 2 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 0be28f6..7c7c9f3 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_BOND +#include <rte_bond.h> +#endif #include "testpmd.h" @@ -393,6 +396,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -2849,6 +2877,518 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) { + printf("\t Failed to set bonding mode for port = %d.\n", port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): " + "Set the bonding mode for port_id", .data = NULL, .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL, }, }; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = + { .f = cmd_set_bonding_balance_xmit_policy_parsed, .help_str = + "set bonding balance_xmit_policy (port_id) (policy_value): " + "Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = + { (void *) &cmd_setbonding_balance_xmit_policy_set, + (void *) &cmd_setbonding_balance_xmit_policy_bonding, + (void *) &cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *) &cmd_setbonding_balance_xmit_policy_port, + (void *) &cmd_setbonding_balance_xmit_policy_policy, + NULL, }, }; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + const uint8_t *slaves; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, &slaves); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) { + printf("%d ", slaves[i]); + } + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, &slaves); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) { + printf("%d ", slaves[i]); + } + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { .f = + cmd_show_bonding_config_parsed, .help_str = + "show bonding config (port_id): " + "Show the bonding config for port_id", .data = NULL, .tokens = { + (void *) &cmd_showbonding_config_show, + (void *) &cmd_showbonding_config_bonding, + (void *) &cmd_showbonding_config_config, + (void *) &cmd_showbonding_config_port, + NULL, }, }; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { .f = + cmd_set_bonding_primary_parsed, .help_str = + "set bonding primary (slave_id) (port_id): " + "Set the primary slave for port_id", .data = NULL, .tokens = { + (void *) &cmd_setbonding_primary_set, + (void *) &cmd_setbonding_primary_bonding, + (void *) &cmd_setbonding_primary_primary, + (void *) &cmd_setbonding_primary_slave, + (void *) &cmd_setbonding_primary_port, + NULL, }, }; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, .help_str = + "add bonding slave (slave_id) (port_id): " + "Add a slave device to a bonded device", .data = NULL, + .tokens = { (void *) &cmd_addbonding_slave_add, + (void *) &cmd_addbonding_slave_bonding, + (void *) &cmd_addbonding_slave_slave, + (void *) &cmd_addbonding_slave_slaveid, + (void *) &cmd_addbonding_slave_port, + NULL, }, }; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = +TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { .f = + cmd_remove_bonding_slave_parsed, .help_str = + "remove bonding slave (slave_id) (port_id): " + "Remove a slave device from a bonded device", .data = NULL, + .tokens = { (void *) &cmd_removebonding_slave_remove, + (void *) &cmd_removebonding_slave_bonding, + (void *) &cmd_removebonding_slave_slave, + (void *) &cmd_removebonding_slave_slaveid, + (void *) &cmd_removebonding_slave_port, + NULL, }, }; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + + int port_id; + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + port_id = rte_eth_bond_create("testpmd-bonded-dev", res->mode, res->socket); + /* Create a new bonded device. */ + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("\t Created new bonded device (port %d).\n", port_id); + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = +TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = +TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = +TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = +TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = +TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = + { .f = cmd_create_bonded_device_parsed, + .help_str = + "create bonded device (mode) (socket): " + "Create a new bonded device with specific bonding mode and socket", + .data = NULL, .tokens = { + (void *) &cmd_createbonded_device_create, + (void *) &cmd_createbonded_device_bonded, + (void *) &cmd_createbonded_device_device, + (void *) &cmd_createbonded_device_mode, + (void *) &cmd_createbonded_device_socket, + NULL, }, }; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, + "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = +TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = +TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = +TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, .data = (void *) 0, .help_str = + "set bonding mac_addr (port_id) (address): ", .tokens = { + (void *) &cmd_set_bond_mac_addr_set, + (void *) &cmd_set_bond_mac_addr_bonding, + (void *) &cmd_set_bond_mac_addr_mac, + (void *) &cmd_set_bond_mac_addr_portnum, + (void *) &cmd_set_bond_mac_addr_addr, + NULL, }, }; + +#endif /* RTE_LIBRTE_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -5333,6 +5873,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 7a60048..6b44ef0 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,7 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif - +#ifdef RTE_LIBRTE_BOND +#include <rte_bond.h> +#endif #include "testpmd.h" static void diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index bc38305..a49b322 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) " + "failed\n", nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 0cf5a92..1f54787 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH 4/4] Add Link Bonding Library to Doxygen 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty ` (2 preceding siblings ...) 2014-05-28 15:32 ` [dpdk-dev] [PATCH 3/4] Link bonding integration into testpmd declan.doherty @ 2014-05-28 15:32 ` declan.doherty 2014-05-28 17:49 ` [dpdk-dev] [PATCH 0/4] Link Bonding Library Neil Horman ` (3 subsequent siblings) 7 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-05-28 15:32 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 2825c08..2206c68 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index 642f77a..a9c5b30 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_bond \ lib/librte_eal/common/include \ lib/librte_ether \ lib/librte_hash \ -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty ` (3 preceding siblings ...) 2014-05-28 15:32 ` [dpdk-dev] [PATCH 4/4] Add Link Bonding Library to Doxygen declan.doherty @ 2014-05-28 17:49 ` Neil Horman 2014-05-29 10:33 ` Doherty, Declan 2014-05-29 3:23 ` Cao, Waterman ` (2 subsequent siblings) 7 siblings, 1 reply; 127+ messages in thread From: Neil Horman @ 2014-05-28 17:49 UTC (permalink / raw) To: declan.doherty; +Cc: dev On Wed, May 28, 2014 at 04:32:00PM +0100, declan.doherty@intel.com wrote: > From: Declan Doherty <declan.doherty@intel.com> > > Initial release of Link Bonding Library (lib/librte_bond) with support for > bonding modes : > 0 - Round Robin > 1 - Active Backup > 2 - Balance l2 / l23 / l34 > 3 - Broadcast > Why make this a separate library? That requires exposure of yet another API to applications. Instead, why not write a PMD that can enslave other PMD's and treat them all as a single interface? That way this all works with the existing API. Neil > patches split: > 1 - library + makefile changes > 2 - Unit test suite, including code to generate packet bursts for > testing rx and tx functionality of bonded device and a > virtual/stubbed out ethdev for use as slave ethdev in testing > 3 - Link bonding integration into testpmd, including : > - Includes the ability to create new bonded devices. > - Add /remove bonding slave devices. > - Interogate bonded device stats/configuration > - Change bonding modes and select balance transmit polices > 4 - Add Link Bonding Library to Doxygen > > > app/test-pmd/cmdline.c | 550 +++++ > app/test-pmd/parameters.c | 4 +- > app/test-pmd/testpmd.c | 28 +- > app/test-pmd/testpmd.h | 2 + > app/test/Makefile | 3 + > app/test/commands.c | 3 + > app/test/packet_burst_generator.c | 276 +++ > app/test/packet_burst_generator.h | 85 + > app/test/test.h | 1 + > app/test/test_link_bonding.c | 4007 +++++++++++++++++++++++++++++++++++++ > app/test/virtual_pmd.c | 580 ++++++ > app/test/virtual_pmd.h | 74 + > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_bond/Makefile | 28 + > lib/librte_bond/rte_bond.c | 1679 ++++++++++++++++ > lib/librte_bond/rte_bond.h | 228 +++ > mk/rte.app.mk | 5 + > 21 files changed, 7564 insertions(+), 2 deletions(-) > create mode 100644 app/test/packet_burst_generator.c > create mode 100644 app/test/packet_burst_generator.h > create mode 100644 app/test/test_link_bonding.c > create mode 100644 app/test/virtual_pmd.c > create mode 100644 app/test/virtual_pmd.h > create mode 100644 lib/librte_bond/Makefile > create mode 100644 lib/librte_bond/rte_bond.c > create mode 100644 lib/librte_bond/rte_bond.h > > -- > 1.8.5.3 > > ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library 2014-05-28 17:49 ` [dpdk-dev] [PATCH 0/4] Link Bonding Library Neil Horman @ 2014-05-29 10:33 ` Doherty, Declan 2014-05-29 11:33 ` Neil Horman 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-05-29 10:33 UTC (permalink / raw) To: Neil Horman; +Cc: dev -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Wednesday, May 28, 2014 6:49 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > On Wed, May 28, 2014 at 04:32:00PM +0100, declan.doherty@intel.com wrote: > > From: Declan Doherty <declan.doherty@intel.com> > > > > Initial release of Link Bonding Library (lib/librte_bond) with support > > for bonding modes : > > 0 - Round Robin > > 1 - Active Backup > > 2 - Balance l2 / l23 / l34 > > 3 - Broadcast > > > Why make this a separate library? That requires exposure of yet another API to applications. Instead, why > not write a PMD that can enslave other PMD's and treat them all as a single interface? That way this all > > works with the existing API. > > Neil Hi Neil, the link bonding device is essentially a software PMD, and as such supports all the standard PMD APIs, the only new APIs which the link bonding library introduces are for the control operations of the bonded device which are currently unsupported by the standard PMD API. Operations such as creating, adding/removing slaves, and configuring the modes of operation of the device have no analogous APIs in the current PMD API and required new ones to be created . Declan -------------------------------------------------------------- Intel Shannon Limited Registered in Ireland Registered Office: Collinstown Industrial Park, Leixlip, County Kildare Registered Number: 308263 Business address: Dromore House, East Park, Shannon, Co. Clare This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library 2014-05-29 10:33 ` Doherty, Declan @ 2014-05-29 11:33 ` Neil Horman 0 siblings, 0 replies; 127+ messages in thread From: Neil Horman @ 2014-05-29 11:33 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev On Thu, May 29, 2014 at 10:33:00AM +0000, Doherty, Declan wrote: > -----Original Message----- > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > Sent: Wednesday, May 28, 2014 6:49 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > On Wed, May 28, 2014 at 04:32:00PM +0100, declan.doherty@intel.com wrote: > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > Initial release of Link Bonding Library (lib/librte_bond) with support > > > for bonding modes : > > > 0 - Round Robin > > > 1 - Active Backup > > > 2 - Balance l2 / l23 / l34 > > > 3 - Broadcast > > > > > Why make this a separate library? That requires exposure of yet another API to applications. Instead, why > not write a PMD that can enslave other PMD's and treat them all as a single interface? That way this all > > works with the existing API. > > > > Neil > > Hi Neil, > the link bonding device is essentially a software PMD, and as such supports all the standard PMD APIs, the only new APIs which the link bonding library introduces are for the control operations of the bonded device which are currently unsupported by the standard PMD API. Operations such as creating, adding/removing slaves, and configuring the modes of operation of the device have no analogous APIs in the current PMD API and required new ones to be created . Thats really only true in spirit, in the sense that this library transmits and receives frames like a PMD does. In practice it doesn't work and isn't architected the same way. You don't register the driver using the same method as the other PMDs, which means that using --vdev on the command line wont work for this type of device. It also implies that applications have to be made specifically aware of the fact that they are using a bonded interface (i.e. they need to call the bonding setup routines to create the bond). I would recommend: 1) Register the pmd using the PMD_DRIVER_REGISTER macro, like other PMD's 2) Use the kvargs library to support configuration via the --vdev command line option, so bonds can be created administratively, rather than just programatically 3) Separate the command api from the PMD functionality into separate libraries (use control mbufs to communicate configuration changes to the pmd). This will allow users to dynamically load the pmd functionality (without compile or run time linking requirements), and then optionally use the programatic interface (or not if they want to save memory) Regards Neil > > Declan > -------------------------------------------------------------- > Intel Shannon Limited > Registered in Ireland > Registered Office: Collinstown Industrial Park, Leixlip, County Kildare > Registered Number: 308263 > Business address: Dromore House, East Park, Shannon, Co. Clare > > This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. > > > ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty ` (4 preceding siblings ...) 2014-05-28 17:49 ` [dpdk-dev] [PATCH 0/4] Link Bonding Library Neil Horman @ 2014-05-29 3:23 ` Cao, Waterman 2014-05-29 10:35 ` Doherty, Declan 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty 7 siblings, 1 reply; 127+ messages in thread From: Cao, Waterman @ 2014-05-29 3:23 UTC (permalink / raw) To: Doherty, Declan, dev, dev Hi declan, Do you send out Patch 1,2, 3 for link bonding? Only see patch 0 and 4. Thanks Waterman -----Original Message----- From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of declan.doherty@intel.com Sent: Wednesday, May 28, 2014 11:32 PM To: dev@dpdk.org; dev@dpdk.org Subject: [dpdk-dev] [PATCH 0/4] Link Bonding Library From: Declan Doherty <declan.doherty@intel.com> Initial release of Link Bonding Library (lib/librte_bond) with support for bonding modes : 0 - Round Robin 1 - Active Backup 2 - Balance l2 / l23 / l34 3 - Broadcast patches split: 1 - library + makefile changes 2 - Unit test suite, including code to generate packet bursts for testing rx and tx functionality of bonded device and a virtual/stubbed out ethdev for use as slave ethdev in testing 3 - Link bonding integration into testpmd, including : - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices 4 - Add Link Bonding Library to Doxygen app/test-pmd/cmdline.c | 550 +++++ app/test-pmd/parameters.c | 4 +- app/test-pmd/testpmd.c | 28 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 3 + app/test/commands.c | 3 + app/test/packet_burst_generator.c | 276 +++ app/test/packet_burst_generator.h | 85 + app/test/test.h | 1 + app/test/test_link_bonding.c | 4007 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 580 ++++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_bond/Makefile | 28 + lib/librte_bond/rte_bond.c | 1679 ++++++++++++++++ lib/librte_bond/rte_bond.h | 228 +++ mk/rte.app.mk | 5 + 21 files changed, 7564 insertions(+), 2 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c create mode 100644 lib/librte_bond/rte_bond.h -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library 2014-05-29 3:23 ` Cao, Waterman @ 2014-05-29 10:35 ` Doherty, Declan 0 siblings, 0 replies; 127+ messages in thread From: Doherty, Declan @ 2014-05-29 10:35 UTC (permalink / raw) To: Cao, Waterman, dev, dev > From: Cao, Waterman > Sent: Thursday, May 29, 2014 4:23 AM > To: Doherty, Declan; dev@dpdk.org; dev@dpdk.org > Cc: Cao, Waterman > Subject: RE: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > Hi declan, > > Do you send out Patch 1,2, 3 for link bonding? > Only see patch 0 and 4. > > Thanks > Waterman Hi Waterman, As far as I can see all the patches have been submitted, and are visible on the mailing list archives. Declan -------------------------------------------------------------- Intel Shannon Limited Registered in Ireland Registered Office: Collinstown Industrial Park, Leixlip, County Kildare Registered Number: 308263 Business address: Dromore House, East Park, Shannon, Co. Clare This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty ` (5 preceding siblings ...) 2014-05-29 3:23 ` Cao, Waterman @ 2014-06-04 15:18 ` declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty ` (9 more replies) 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty 7 siblings, 10 replies; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> v2 patch additions, fix for tx burst broadcast, incrementing the reference count on each mbuf by the number of slaves - 1 add/remove slave behavior chnange to fix primary slave port assignment patchcheck code fixes Initial release of Link Bonding Library (lib/librte_bond) with support for bonding modes : 0 - Round Robin 1 - Active Backup 2 - Balance l2 / l23 / l34 3 - Broadcast patches split: 1 - library + makefile changes 2 - Unit test suite, including code to generate packet bursts for testing rx and tx functionality of bonded device and a virtual/stubbed out ethdev for use as slave ethdev in testing 3 - Link bonding integration into testpmd, including : - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices 4 - Add Link Bonding Library to Doxygen app/test-pmd/cmdline.c | 570 ++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 4 +- app/test-pmd/testpmd.c | 37 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 3 + app/test/commands.c | 3 + app/test/packet_burst_generator.c | 289 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3943 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_bond/Makefile | 28 + lib/librte_bond/rte_bond.c | 1682 ++++++++++++++++ lib/librte_bond/rte_bond.h | 228 +++ mk/rte.app.mk | 5 + 22 files changed, 7531 insertions(+), 7 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c create mode 100644 lib/librte_bond/rte_bond.h -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 1/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty @ 2014-06-04 15:18 ` declan.doherty 2014-06-04 15:18 ` declan.doherty ` (2 more replies) 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 2/4] Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing - checkpack fixes declan.doherty ` (8 subsequent siblings) 9 siblings, 3 replies; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> - Broadcast TX burst broadcast bug fix - Add/remove slave behavior fix - Checkpatch fixes Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_bond/Makefile | 28 + lib/librte_bond/rte_bond.c | 1682 ++++++++++++++++++++++++++++++++++++++++++++ lib/librte_bond/rte_bond.h | 228 ++++++ mk/rte.app.mk | 5 + 7 files changed, 1954 insertions(+) create mode 100644 lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c create mode 100644 lib/librte_bond/rte_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 2cc7b80..53ed8b9 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -187,6 +187,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding library +# +CONFIG_RTE_LIBRTE_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 62619c6..35b525a 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -211,6 +211,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n +# +# Compile link bonding library +# +CONFIG_RTE_LIBRTE_BOND=y + CONFIG_RTE_LIBRTE_PMD_XENVIRT=n # diff --git a/lib/Makefile b/lib/Makefile index b92b392..9995ba8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -47,6 +47,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_BOND) += librte_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_NET) += librte_net diff --git a/lib/librte_bond/Makefile b/lib/librte_bond/Makefile new file mode 100644 index 0000000..7514378 --- /dev/null +++ b/lib/librte_bond/Makefile @@ -0,0 +1,28 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_BOND) += rte_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_bond.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_BOND) += lib/librte_mbuf lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_BOND) += lib/librte_malloc + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_bond/rte_bond.c b/lib/librte_bond/rte_bond.c new file mode 100644 index 0000000..c079b89 --- /dev/null +++ b/lib/librte_bond/rte_bond.c @@ -0,0 +1,1682 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_cycles.h> +#include <rte_ip.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_bond.h" + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; /**< Queue Id */ + struct bond_dev_private *dev_private; /**< Reference to eth_dev private + structure */ + + uint16_t nb_rx_desc; /**< Number of RX descriptors + available for the queue */ + struct rte_eth_rxconf rx_conf; /**< Copy of RX configuration + structure for queue */ + struct rte_mempool *mb_pool; /**< Reference to mbuf pool to use + for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; /**< Queue Id */ + struct bond_dev_private *dev_private; /**< Reference to dev private + structure */ + uint16_t nb_tx_desc; /**< Number of TX descriptors + available for the queue */ + struct rte_eth_txconf tx_conf; /**< Copy of TX configuration + structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; /**< Slave eth_dev original MAC address */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t balance_xmit_policy; /**< Transmit policy - l2 / l23 / l34 + for operation in balance mode */ + uint8_t user_defined_mac; /**< Flag for whether MAC address is + user defined or not */ + uint8_t promiscuous_en; /**< Enabled/disable promiscuous mode on + slave devices */ + uint8_t link_props_set; /**< Bonded eth_dev link properties set*/ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int last_slave_idx = -1; + int i, slave_idx; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + slave_idx = i % num_of_slaves; + slave_bufs[slave_idx][(slave_nb_pkts[slave_idx])++] = bufs[i]; + } + + /* calculate the next slave to transmit on based on the last slave idx used + * in the last call to bond_ethdev_tx_burst_round_robin */ + slave_idx = last_slave_idx + 1; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + slave_idx = (slave_idx + i) % num_of_slaves; + + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[slave_idx], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + } + } + + last_slave_idx = slave_idx; + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->primary_port) { + if (internals->active_slave_count > 0) + internals->primary_port = internals->active_slaves[0]; + else + internals->primary_port = internals->slaves[0]; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + + + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + int i; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) { + /* Found slave device in active slave list */ + internals->primary_port = slave_port_id; + return 0; + } + } + + /* Slave is not bound to this master device */ + return -1; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->primary_port; +} + +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + *slaves = (uint8_t *)(&internals->slaves); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + *slaves = (uint8_t *)(&internals->active_slaves); + + return internals->active_slave_count; + +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_bond/rte_bond.h b/lib/librte_bond/rte_bond.h new file mode 100644 index 0000000..97b6d5e --- /dev/null +++ b/lib/librte_bond/rte_bond.h @@ -0,0 +1,228 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/** Link Bonding Mode Definitions */ +#define BONDING_MODE_ROUND_ROBIN (0) +#define BONDING_MODE_ACTIVE_BACKUP (1) +#define BONDING_MODE_BALANCE (2) +#define BONDING_MODE_BROADCAST (3) + +/** Balance Mode Transmit Policy Types */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +#define BALANCE_XMIT_POLICY_LAYER23 (1) +#define BALANCE_XMIT_POLICY_LAYER34 (2) + +/** + * Create a bonded rte_eth_dev device + * + * @param name + * @param mode + * @param socket_id + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id + * @param mode + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id + * @param slaves + * + * @return + * number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id + * @param slaves + * + * @return + * number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id + * @param mac_addr + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode + * + * @param bonded_port_id + * @param policy + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id + * + * @return + * balance transmit policy on success, negative value otherwise + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index a836577..a803a5c 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -177,8 +177,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_BOND),y) +LDLIBS += -lrte_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 1/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty @ 2014-06-04 15:18 ` declan.doherty 2014-06-05 15:15 ` Stephen Hemminger 2014-06-09 21:11 ` Eric Kinzie 2 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> - Broadcast TX burst broadcast bug fix - Add/remove slave behavior fix - Checkpatch fixes Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_bond/Makefile | 28 + lib/librte_bond/rte_bond.c | 1682 ++++++++++++++++++++++++++++++++++++++++++++ lib/librte_bond/rte_bond.h | 228 ++++++ mk/rte.app.mk | 5 + 7 files changed, 1954 insertions(+) create mode 100644 lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c create mode 100644 lib/librte_bond/rte_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 2cc7b80..53ed8b9 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -187,6 +187,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding library +# +CONFIG_RTE_LIBRTE_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 62619c6..35b525a 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -211,6 +211,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n +# +# Compile link bonding library +# +CONFIG_RTE_LIBRTE_BOND=y + CONFIG_RTE_LIBRTE_PMD_XENVIRT=n # diff --git a/lib/Makefile b/lib/Makefile index b92b392..9995ba8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -47,6 +47,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_BOND) += librte_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_NET) += librte_net diff --git a/lib/librte_bond/Makefile b/lib/librte_bond/Makefile new file mode 100644 index 0000000..7514378 --- /dev/null +++ b/lib/librte_bond/Makefile @@ -0,0 +1,28 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_BOND) += rte_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_bond.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_BOND) += lib/librte_mbuf lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_BOND) += lib/librte_malloc + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_bond/rte_bond.c b/lib/librte_bond/rte_bond.c new file mode 100644 index 0000000..c079b89 --- /dev/null +++ b/lib/librte_bond/rte_bond.c @@ -0,0 +1,1682 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_cycles.h> +#include <rte_ip.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_bond.h" + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; /**< Queue Id */ + struct bond_dev_private *dev_private; /**< Reference to eth_dev private + structure */ + + uint16_t nb_rx_desc; /**< Number of RX descriptors + available for the queue */ + struct rte_eth_rxconf rx_conf; /**< Copy of RX configuration + structure for queue */ + struct rte_mempool *mb_pool; /**< Reference to mbuf pool to use + for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; /**< Queue Id */ + struct bond_dev_private *dev_private; /**< Reference to dev private + structure */ + uint16_t nb_tx_desc; /**< Number of TX descriptors + available for the queue */ + struct rte_eth_txconf tx_conf; /**< Copy of TX configuration + structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; /**< Slave eth_dev original MAC address */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t balance_xmit_policy; /**< Transmit policy - l2 / l23 / l34 + for operation in balance mode */ + uint8_t user_defined_mac; /**< Flag for whether MAC address is + user defined or not */ + uint8_t promiscuous_en; /**< Enabled/disable promiscuous mode on + slave devices */ + uint8_t link_props_set; /**< Bonded eth_dev link properties set*/ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int last_slave_idx = -1; + int i, slave_idx; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + slave_idx = i % num_of_slaves; + slave_bufs[slave_idx][(slave_nb_pkts[slave_idx])++] = bufs[i]; + } + + /* calculate the next slave to transmit on based on the last slave idx used + * in the last call to bond_ethdev_tx_burst_round_robin */ + slave_idx = last_slave_idx + 1; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + slave_idx = (slave_idx + i) % num_of_slaves; + + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[slave_idx], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + } + } + + last_slave_idx = slave_idx; + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->primary_port) { + if (internals->active_slave_count > 0) + internals->primary_port = internals->active_slaves[0]; + else + internals->primary_port = internals->slaves[0]; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + + + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + int i; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) { + /* Found slave device in active slave list */ + internals->primary_port = slave_port_id; + return 0; + } + } + + /* Slave is not bound to this master device */ + return -1; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->primary_port; +} + +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + *slaves = (uint8_t *)(&internals->slaves); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + *slaves = (uint8_t *)(&internals->active_slaves); + + return internals->active_slave_count; + +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_bond/rte_bond.h b/lib/librte_bond/rte_bond.h new file mode 100644 index 0000000..97b6d5e --- /dev/null +++ b/lib/librte_bond/rte_bond.h @@ -0,0 +1,228 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/** Link Bonding Mode Definitions */ +#define BONDING_MODE_ROUND_ROBIN (0) +#define BONDING_MODE_ACTIVE_BACKUP (1) +#define BONDING_MODE_BALANCE (2) +#define BONDING_MODE_BROADCAST (3) + +/** Balance Mode Transmit Policy Types */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +#define BALANCE_XMIT_POLICY_LAYER23 (1) +#define BALANCE_XMIT_POLICY_LAYER34 (2) + +/** + * Create a bonded rte_eth_dev device + * + * @param name + * @param mode + * @param socket_id + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id + * @param mode + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id + * @param slave_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id + * @param slaves + * + * @return + * number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id + * @param slaves + * + * @return + * number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, const uint8_t **slaves); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id + * @param mac_addr + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode + * + * @param bonded_port_id + * @param policy + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id + * + * @return + * balance transmit policy on success, negative value otherwise + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index a836577..a803a5c 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -177,8 +177,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_BOND),y) +LDLIBS += -lrte_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty 2014-06-04 15:18 ` declan.doherty @ 2014-06-05 15:15 ` Stephen Hemminger 2014-06-06 9:07 ` Doherty, Declan 2014-06-09 21:11 ` Eric Kinzie 2 siblings, 1 reply; 127+ messages in thread From: Stephen Hemminger @ 2014-06-05 15:15 UTC (permalink / raw) To: declan.doherty; +Cc: dev On Wed, 4 Jun 2014 16:18:02 +0100 declan.doherty@intel.com wrote: > From: Declan Doherty <declan.doherty@intel.com> > > - Broadcast TX burst broadcast bug fix > - Add/remove slave behavior fix > - Checkpatch fixes > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> There are some pretty weak hash functions in there. What about using: --- a/lib/librte_eal/common/include/rte_common.h 2014-02-27 09:02:23.424698279 -0800 +++ b/lib/librte_eal/common/include/rte_common.h 2014-06-05 08:03:51.615839548 -0700 @@ -365,6 +365,31 @@ rte_str_to_size(const char *str) } /** + * Multiplicative hash functions useful to distrbute + * a uniform set of consective keys + * + * @param val + * The input uniform key + * @param bits + * Number of bits desired + */ +#define GOLDEN_RATIO_32 2654404609U +static inline uint32_t +rte_fib_hash32(uint32_t val, unsigned int bits) +{ + val *= GOLDEN_RATIO_32; + return val >> (32 - bits); +} + +#define GOLDEN_RATIO_64 0x9e37fffffffc0001UL +static inline uint64_t +rte_fib_hash64(uint64_t val, unsigned bits) +{ + val *= GOLDEN_RATIO_64; + return val >> (64 - bits); +} + +/** * Function to terminate the application immediately, printing an error * message and returning the exit_code back to the shell. * --- a/lib/librte_ether/rte_ether.h 2014-02-27 09:02:23.438697987 -0800 +++ b/lib/librte_ether/rte_ether.h 2014-06-05 08:14:15.438080058 -0700 @@ -48,6 +48,8 @@ extern "C" { #include <rte_memcpy.h> #include <rte_random.h> +#include <rte_common.h> +#include <rte_jhash.h> #define ETHER_ADDR_LEN 6 /**< Length of Ethernet address. */ #define ETHER_TYPE_LEN 2 /**< Length of Ethernet type field. */ @@ -250,6 +252,80 @@ struct ether_hdr { uint16_t ether_type; /**< Frame type. */ } __attribute__((__packed__)); + +/* + * These functions work by aliasing the 6 byte Ethernet address + * into a 64 bit value. The macro shift16, removes the extra + * bytes with the correct shift depending on byte order + */ +#ifdef __BYTE_ORDER + #if __BYTE_ORDER == __BIG_ENDIAN + #define shift16(s) (s >>= 16) + #else + #define shift16(s) (s <<= 16) + #endif +#endif + +/** + * Fast compare of Ethernet address. + * + * @param e1 + * A pointer to a ether_addr structure one address + * @param e2 + * A pointer to a ether_addr structure holding other address + * @return + * True (1) if addresses are the same + * false (0) otherwise. + */ +static inline int +ether_addr_equal(const struct ether_addr *e1, const struct ether_addr *e2) +{ + uint64_t e1_addr = *(const uint64_t *) e1; + shift16(e1_addr); + uint64_t e2_addr = *(const uint64_t *) e2; + shift16(e2_addr); + + return (e1_addr == e2_addr); +} + +/** + * Fast hash of ethernet address + * + * @param ea + * A pointer to a ether_addr structure + * @param bits + * Number of bits desired + * @return + * Calculated hash value. + */ +static inline uint32_t +ether_addr_hash(const struct ether_addr *ea, unsigned bits) +{ + uint64_t val = *(const uint64_t *) ea; + + shift16(val); + return rte_fib_hash64(val, bits); +} +#undef shift16 + +/** + * Hash of ethernet source and destination + * + * @param ea + * A pointer to a ether_hdr structure + * @param initval + * Initialising value of hash. + * @return + * Calculated hash value. + */ +static inline uint32_t +ether_header_hash(const struct ether_hdr *hdr, unsigned seed) +{ + const uint32_t *key = (const uint32_t *)hdr; + + return rte_jhash(key, 2*ETHER_ADDR_LEN, seed); +} + /** * Ethernet VLAN Header. * Contains the 16-bit VLAN Tag Control Identifier and the Ethernet type ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library 2014-06-05 15:15 ` Stephen Hemminger @ 2014-06-06 9:07 ` Doherty, Declan 2014-06-06 15:13 ` Stephen Hemminger 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-06 9:07 UTC (permalink / raw) To: dev > -----Original Message----- > From: Stephen Hemminger [mailto:stephen@networkplumber.org] > Sent: Thursday, June 5, 2014 4:16 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library > > On Wed, 4 Jun 2014 16:18:02 +0100 > declan.doherty@intel.com wrote: > > > From: Declan Doherty <declan.doherty@intel.com> > > > > - Broadcast TX burst broadcast bug fix > > - Add/remove slave behavior fix > > - Checkpatch fixes > > > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > > There are some pretty weak hash functions in there. > > What about using: > ..... Hi Stephen, I suppose the naming of these as hash functions is probably misleading, the aim is not to create a unique hash for each flow with minimal collisions but to help balance traffic flows across the slaves of the bonded device, and as we are doing a modulus calculation based on the number of slaves attached, which I believe will typically be in the 2-4 range, I'm not sure that a stronger hashing function will give a better balance of flows across the slaves. For example in the Linux kernel implementation of the link bonding driver the mac hashing only uses the last byte of src & dst mac addresses to calculate it's eth hash. /* L2 hash helper */ static inline u32 bond_eth_hash(struct sk_buff *skb) { struct ethhdr *data = (struct ethhdr *)skb->data; if (skb_headlen(skb) >= offsetof(struct ethhdr, h_proto)) return data->h_dest[5] ^ data->h_source[5]; return 0; } I'll investigate and see if a stronger hashing function will result in a better balancing of flows. -------------------------------------------------------------- Intel Shannon Limited Registered in Ireland Registered Office: Collinstown Industrial Park, Leixlip, County Kildare Registered Number: 308263 Business address: Dromore House, East Park, Shannon, Co. Clare This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library 2014-06-06 9:07 ` Doherty, Declan @ 2014-06-06 15:13 ` Stephen Hemminger 0 siblings, 0 replies; 127+ messages in thread From: Stephen Hemminger @ 2014-06-06 15:13 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev On Fri, 6 Jun 2014 09:07:23 +0000 "Doherty, Declan" <declan.doherty@intel.com> wrote: > > -----Original Message----- > > From: Stephen Hemminger [mailto:stephen@networkplumber.org] > > Sent: Thursday, June 5, 2014 4:16 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library > > > > On Wed, 4 Jun 2014 16:18:02 +0100 > > declan.doherty@intel.com wrote: > > > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > - Broadcast TX burst broadcast bug fix > > > - Add/remove slave behavior fix > > > - Checkpatch fixes > > > > > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > > > > There are some pretty weak hash functions in there. > > > > What about using: > > ..... > > Hi Stephen, > > I suppose the naming of these as hash functions is probably misleading, the aim is not to create a unique hash for each flow with minimal collisions but to help balance traffic flows across the slaves of the bonded device, and as we are doing a modulus calculation based on the number of slaves attached, which I believe will typically be in the 2-4 range, I'm not sure that a stronger hashing function will give a better balance of flows across the slaves. For example in the Linux kernel implementation of the link bonding driver the mac hashing only uses the last byte of src & dst mac addresses to calculate it's eth hash. > > /* L2 hash helper */ > static inline u32 bond_eth_hash(struct sk_buff *skb) > { > struct ethhdr *data = (struct ethhdr *)skb->data; > > if (skb_headlen(skb) >= offsetof(struct ethhdr, h_proto)) > return data->h_dest[5] ^ data->h_source[5]; > > return 0; > } > > I'll investigate and see if a stronger hashing function will result in a better balancing of flows. > -------------------------------------------------------------- > Intel Shannon Limited > Registered in Ireland > Registered Office: Collinstown Industrial Park, Leixlip, County Kildare > Registered Number: 308263 > Business address: Dromore House, East Park, Shannon, Co. Clare > > This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. > > The ether hash is very quick using multplicative hash on 64 bit value. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty 2014-06-04 15:18 ` declan.doherty 2014-06-05 15:15 ` Stephen Hemminger @ 2014-06-09 21:11 ` Eric Kinzie 2014-06-13 14:03 ` Doherty, Declan 2 siblings, 1 reply; 127+ messages in thread From: Eric Kinzie @ 2014-06-09 21:11 UTC (permalink / raw) To: Declan Doherty; +Cc: dev On Wed Jun 04 16:18:53 +0100 2014, Declan Doherty wrote: > - Broadcast TX burst broadcast bug fix > - Add/remove slave behavior fix > - Checkpatch fixes Declan, would you consider the following change to rte_bond.c? The two header files from librte_cmdline don't seem to be necessary. Eric --- a/lib/librte_bond/rte_bond.c +++ b/lib/librte_bond/rte_bond.c @@ -44,9 +44,6 @@ #include <rte_ip.h> #include <rte_udp.h> -#include <cmdline_parse.h> -#include <cmdline_parse_etheraddr.h> - #include "rte_bond.h" static const char *driver_name = "Link Bonding PMD"; ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library 2014-06-09 21:11 ` Eric Kinzie @ 2014-06-13 14:03 ` Doherty, Declan 0 siblings, 0 replies; 127+ messages in thread From: Doherty, Declan @ 2014-06-13 14:03 UTC (permalink / raw) To: Eric Kinzie; +Cc: dev > -----Original Message----- > From: Eric Kinzie [mailto:ehkinzie@gmail.com] > Sent: Monday, June 9, 2014 10:11 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v2 1/4] Link Bonding Library > > On Wed Jun 04 16:18:53 +0100 2014, Declan Doherty wrote: > > - Broadcast TX burst broadcast bug fix > > - Add/remove slave behavior fix > > - Checkpatch fixes > > Declan, would you consider the following change to rte_bond.c? The two > header files from librte_cmdline don't seem to be necessary. > > Eric > > > --- a/lib/librte_bond/rte_bond.c > +++ b/lib/librte_bond/rte_bond.c > @@ -44,9 +44,6 @@ > #include <rte_ip.h> > #include <rte_udp.h> > > -#include <cmdline_parse.h> > -#include <cmdline_parse_etheraddr.h> > - > #include "rte_bond.h" > > static const char *driver_name = "Link Bonding PMD"; Hi Eric, I've cleaned up the includes in v3 of the patch, but the those headers are now actually required for the kvarg parsing. Regards Declan ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 2/4] Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing - checkpack fixes 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty @ 2014-06-04 15:18 ` declan.doherty 2014-06-04 15:18 ` declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library declan.doherty ` (7 subsequent siblings) 9 siblings, 1 reply; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 3 + app/test/commands.c | 3 + app/test/packet_burst_generator.c | 289 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3943 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4965 insertions(+) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index b49785e..ac55a11 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -56,6 +56,7 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ring_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_rwlock.c SRCS-$(CONFIG_RTE_APP_TEST) += test_timer.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mbuf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_logs.c @@ -94,6 +95,8 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_common.c SRCS-$(CONFIG_RTE_APP_TEST) += test_timer_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c diff --git a/app/test/commands.c b/app/test/commands.c index efa8566..4d0ec3b 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -157,6 +157,8 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -221,6 +223,7 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" + "link_bonding_autotest#" "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..8c535f6 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,289 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + if (ip_cksum > 65535) + ip_cksum -= 65535; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} + diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index 1945d29..7213e1a 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -94,6 +94,7 @@ int test_pmd_ring(void); int test_ivshmem(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..461d132 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3943 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + test_params->slave_port_ids[i] = virtual_ethdev_create("slave_pmd_1", + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + const uint8_t *slaves; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + const uint8_t *slaves; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, &slaves); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + &slaves); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + const uint8_t *slaves; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + const uint8_t *slaves; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + const uint8_t *slaves; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves) != TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) + != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) + != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) != + 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..f2ef084 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 2/4] Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing - checkpack fixes 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 2/4] Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing - checkpack fixes declan.doherty @ 2014-06-04 15:18 ` declan.doherty 0 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 3 + app/test/commands.c | 3 + app/test/packet_burst_generator.c | 289 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3943 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4965 insertions(+) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index b49785e..ac55a11 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -56,6 +56,7 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ring_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_rwlock.c SRCS-$(CONFIG_RTE_APP_TEST) += test_timer.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mempool_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_mbuf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_logs.c @@ -94,6 +95,8 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_common.c SRCS-$(CONFIG_RTE_APP_TEST) += test_timer_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c diff --git a/app/test/commands.c b/app/test/commands.c index efa8566..4d0ec3b 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -157,6 +157,8 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -221,6 +223,7 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" + "link_bonding_autotest#" "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..8c535f6 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,289 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + if (ip_cksum > 65535) + ip_cksum -= 65535; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} + diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index 1945d29..7213e1a 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -94,6 +94,7 @@ int test_pmd_ring(void); int test_ivshmem(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..461d132 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3943 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + test_params->slave_port_ids[i] = virtual_ethdev_create("slave_pmd_1", + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + const uint8_t *slaves; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + const uint8_t *slaves; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + const uint8_t *slaves; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, &slaves); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + &slaves); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + const uint8_t *slaves; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + const uint8_t *slaves; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + &slaves); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + const uint8_t *slaves; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves) != TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) + != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) + != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, &slaves) != + 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + const uint8_t *slaves; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, &slaves); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + &slaves); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..f2ef084 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 2/4] Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing - checkpack fixes declan.doherty @ 2014-06-04 15:18 ` declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 3/4] Adding link bonding support to testpmd. - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices declan.doherty ` (6 subsequent siblings) 9 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> v2 patch additions, fix for tx burst broadcast, incrementing the reference count on each mbuf by the number of slaves - 1 add/remove slave behavior chnange to fix primary slave port assignment patchcheck code fixes Initial release of Link Bonding Library (lib/librte_bond) with support for bonding modes : 0 - Round Robin 1 - Active Backup 2 - Balance l2 / l23 / l34 3 - Broadcast patches split: 1 - library + makefile changes 2 - Unit test suite, including code to generate packet bursts for testing rx and tx functionality of bonded device and a virtual/stubbed out ethdev for use as slave ethdev in testing 3 - Link bonding integration into testpmd, including : - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices 4 - Add Link Bonding Library to Doxygen app/test-pmd/cmdline.c | 570 ++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 4 +- app/test-pmd/testpmd.c | 37 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 3 + app/test/commands.c | 3 + app/test/packet_burst_generator.c | 289 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3943 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_bond/Makefile | 28 + lib/librte_bond/rte_bond.c | 1682 ++++++++++++++++ lib/librte_bond/rte_bond.h | 228 +++ mk/rte.app.mk | 5 + 22 files changed, 7531 insertions(+), 7 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c create mode 100644 lib/librte_bond/rte_bond.h -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 3/4] Adding link bonding support to testpmd. - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty ` (2 preceding siblings ...) 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library declan.doherty @ 2014-06-04 15:18 ` declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 4/4] Add Link Bonding Library to Doxygen declan.doherty ` (5 subsequent siblings) 9 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> - Display of port mac address fix - Checkpatch fixes Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 570 ++++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 4 +- app/test-pmd/testpmd.c | 37 ++- app/test-pmd/testpmd.h | 2 + 5 files changed, 610 insertions(+), 7 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 0be28f6..27909f3 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_BOND +#include <rte_bond.h> +#endif #include "testpmd.h" @@ -393,6 +396,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -2849,6 +2877,538 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + const uint8_t *slaves; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, &slaves); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, &slaves); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + + int port_id; + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + port_id = rte_eth_bond_create("testpmd-bonded-dev", res->mode, res->socket); + /* Create a new bonded device. */ + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("\t Created new bonded device (port %d).\n", port_id); + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -5333,6 +5893,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index d6291e7..1ef9a39 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -242,6 +242,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -255,7 +256,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 7a60048..6b44ef0 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,7 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif - +#ifdef RTE_LIBRTE_BOND +#include <rte_bond.h> +#endif #include "testpmd.h" static void diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index bc38305..823c973 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1232,7 +1258,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1360,10 +1386,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 0cf5a92..1f54787 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v2 4/4] Add Link Bonding Library to Doxygen 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty ` (3 preceding siblings ...) 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 3/4] Adding link bonding support to testpmd. - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices declan.doherty @ 2014-06-04 15:18 ` declan.doherty 2014-06-04 16:10 ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library Doherty, Declan ` (4 subsequent siblings) 9 siblings, 0 replies; 127+ messages in thread From: declan.doherty @ 2014-06-04 15:18 UTC (permalink / raw) To: dev, dev From: Declan Doherty <declan.doherty@intel.com> Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 2825c08..2206c68 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index 642f77a..a9c5b30 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_bond \ lib/librte_eal/common/include \ lib/librte_ether \ lib/librte_hash \ -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty ` (4 preceding siblings ...) 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 4/4] Add Link Bonding Library to Doxygen declan.doherty @ 2014-06-04 16:10 ` Doherty, Declan 2014-06-05 8:03 ` De Lara Guarch, Pablo ` (3 subsequent siblings) 9 siblings, 0 replies; 127+ messages in thread From: Doherty, Declan @ 2014-06-04 16:10 UTC (permalink / raw) To: dev Sorry for the double submission of the first 3 parts of this patch set. It was meant to be a test directed to my own email account. Regards Declan > -----Original Message----- > From: Doherty, Declan > Sent: Wednesday, June 4, 2014 4:19 PM > To: dev@dpdk.org; dev@dpdk.org > Cc: Doherty, Declan > Subject: [PATCH v2 0/4] Link Bonding Library > > From: Declan Doherty <declan.doherty@intel.com> > > v2 patch additions, > fix for tx burst broadcast, incrementing the reference count on each mbuf by > the number of slaves - 1 > add/remove slave behavior chnange to fix primary slave port assignment > patchcheck code fixes > > Initial release of Link Bonding Library (lib/librte_bond) with support for > bonding modes : > 0 - Round Robin > 1 - Active Backup > 2 - Balance l2 / l23 / l34 > 3 - Broadcast > > patches split: > 1 - library + makefile changes > 2 - Unit test suite, including code to generate packet bursts for > testing rx and tx functionality of bonded device and a > virtual/stubbed out ethdev for use as slave ethdev in testing > 3 - Link bonding integration into testpmd, including : > - Includes the ability to create new bonded devices. > - Add /remove bonding slave devices. > - Interogate bonded device stats/configuration > - Change bonding modes and select balance transmit polices > 4 - Add Link Bonding Library to Doxygen > > > app/test-pmd/cmdline.c | 570 ++++++ > app/test-pmd/config.c | 4 +- > app/test-pmd/parameters.c | 4 +- > app/test-pmd/testpmd.c | 37 +- > app/test-pmd/testpmd.h | 2 + > app/test/Makefile | 3 + > app/test/commands.c | 3 + > app/test/packet_burst_generator.c | 289 +++ > app/test/packet_burst_generator.h | 78 + > app/test/test.h | 1 + > app/test/test_link_bonding.c | 3943 > +++++++++++++++++++++++++++++++++++++ > app/test/virtual_pmd.c | 574 ++++++ > app/test/virtual_pmd.h | 74 + > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_bond/Makefile | 28 + > lib/librte_bond/rte_bond.c | 1682 ++++++++++++++++ > lib/librte_bond/rte_bond.h | 228 +++ > mk/rte.app.mk | 5 + > 22 files changed, 7531 insertions(+), 7 deletions(-) > create mode 100644 app/test/packet_burst_generator.c > create mode 100644 app/test/packet_burst_generator.h > create mode 100644 app/test/test_link_bonding.c > create mode 100644 app/test/virtual_pmd.c > create mode 100644 app/test/virtual_pmd.h > create mode 100644 lib/librte_bond/Makefile > create mode 100644 lib/librte_bond/rte_bond.c > create mode 100644 lib/librte_bond/rte_bond.h > > -- > 1.8.5.3 -------------------------------------------------------------- Intel Shannon Limited Registered in Ireland Registered Office: Collinstown Industrial Park, Leixlip, County Kildare Registered Number: 308263 Business address: Dromore House, East Park, Shannon, Co. Clare This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty ` (5 preceding siblings ...) 2014-06-04 16:10 ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library Doherty, Declan @ 2014-06-05 8:03 ` De Lara Guarch, Pablo 2014-06-05 11:03 ` Neil Horman ` (2 subsequent siblings) 9 siblings, 0 replies; 127+ messages in thread From: De Lara Guarch, Pablo @ 2014-06-05 8:03 UTC (permalink / raw) To: Doherty, Declan, dev Acked-by: Pablo de Lara Guarch <pablo.de.lara.guarch@intel.com> > -----Original Message----- > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of > declan.doherty@intel.com > Sent: Wednesday, June 04, 2014 4:18 PM > To: dev@dpdk.org; dev@dpdk.org > Subject: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > From: Declan Doherty <declan.doherty@intel.com> > > v2 patch additions, > fix for tx burst broadcast, incrementing the reference count on each mbuf by > the number of slaves - 1 add/remove slave behavior chnange to fix primary > slave port assignment patchcheck code fixes > > Initial release of Link Bonding Library (lib/librte_bond) with support for > bonding modes : > 0 - Round Robin > 1 - Active Backup > 2 - Balance l2 / l23 / l34 > 3 - Broadcast > > patches split: > 1 - library + makefile changes > 2 - Unit test suite, including code to generate packet bursts for > testing rx and tx functionality of bonded device and a > virtual/stubbed out ethdev for use as slave ethdev in testing > 3 - Link bonding integration into testpmd, including : > - Includes the ability to create new bonded devices. > - Add /remove bonding slave devices. > - Interogate bonded device stats/configuration > - Change bonding modes and select balance transmit polices > 4 - Add Link Bonding Library to Doxygen > > > app/test-pmd/cmdline.c | 570 ++++++ > app/test-pmd/config.c | 4 +- > app/test-pmd/parameters.c | 4 +- > app/test-pmd/testpmd.c | 37 +- > app/test-pmd/testpmd.h | 2 + > app/test/Makefile | 3 + > app/test/commands.c | 3 + > app/test/packet_burst_generator.c | 289 +++ > app/test/packet_burst_generator.h | 78 + > app/test/test.h | 1 + > app/test/test_link_bonding.c | 3943 > +++++++++++++++++++++++++++++++++++++ > app/test/virtual_pmd.c | 574 ++++++ > app/test/virtual_pmd.h | 74 + > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_bond/Makefile | 28 + > lib/librte_bond/rte_bond.c | 1682 ++++++++++++++++ > lib/librte_bond/rte_bond.h | 228 +++ > mk/rte.app.mk | 5 + > 22 files changed, 7531 insertions(+), 7 deletions(-) create mode 100644 > app/test/packet_burst_generator.c create mode 100644 > app/test/packet_burst_generator.h create mode 100644 > app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c > create mode 100644 app/test/virtual_pmd.h create mode 100644 > lib/librte_bond/Makefile create mode 100644 lib/librte_bond/rte_bond.c > create mode 100644 lib/librte_bond/rte_bond.h > > -- > 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty ` (6 preceding siblings ...) 2014-06-05 8:03 ` De Lara Guarch, Pablo @ 2014-06-05 11:03 ` Neil Horman 2014-06-06 8:23 ` Doherty, Declan 2014-06-06 3:26 ` Cao, Waterman 2014-06-11 16:33 ` Thomas Monjalon 9 siblings, 1 reply; 127+ messages in thread From: Neil Horman @ 2014-06-05 11:03 UTC (permalink / raw) To: declan.doherty; +Cc: dev On Wed, Jun 04, 2014 at 04:18:01PM +0100, declan.doherty@intel.com wrote: > From: Declan Doherty <declan.doherty@intel.com> > > v2 patch additions, > fix for tx burst broadcast, incrementing the reference count on each mbuf by the number of slaves - 1 > add/remove slave behavior chnange to fix primary slave port assignment > patchcheck code fixes > > Initial release of Link Bonding Library (lib/librte_bond) with support for bonding modes : > 0 - Round Robin > 1 - Active Backup > 2 - Balance l2 / l23 / l34 > 3 - Broadcast > > patches split: > 1 - library + makefile changes > 2 - Unit test suite, including code to generate packet bursts for > testing rx and tx functionality of bonded device and a > virtual/stubbed out ethdev for use as slave ethdev in testing > 3 - Link bonding integration into testpmd, including : > - Includes the ability to create new bonded devices. > - Add /remove bonding slave devices. > - Interogate bonded device stats/configuration > - Change bonding modes and select balance transmit polices > 4 - Add Link Bonding Library to Doxygen > > > app/test-pmd/cmdline.c | 570 ++++++ > app/test-pmd/config.c | 4 +- > app/test-pmd/parameters.c | 4 +- > app/test-pmd/testpmd.c | 37 +- > app/test-pmd/testpmd.h | 2 + > app/test/Makefile | 3 + > app/test/commands.c | 3 + > app/test/packet_burst_generator.c | 289 +++ > app/test/packet_burst_generator.h | 78 + > app/test/test.h | 1 + > app/test/test_link_bonding.c | 3943 +++++++++++++++++++++++++++++++++++++ > app/test/virtual_pmd.c | 574 ++++++ > app/test/virtual_pmd.h | 74 + > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_bond/Makefile | 28 + > lib/librte_bond/rte_bond.c | 1682 ++++++++++++++++ > lib/librte_bond/rte_bond.h | 228 +++ > mk/rte.app.mk | 5 + > 22 files changed, 7531 insertions(+), 7 deletions(-) > create mode 100644 app/test/packet_burst_generator.c > create mode 100644 app/test/packet_burst_generator.h > create mode 100644 app/test/test_link_bonding.c > create mode 100644 app/test/virtual_pmd.c > create mode 100644 app/test/virtual_pmd.h > create mode 100644 lib/librte_bond/Makefile > create mode 100644 lib/librte_bond/rte_bond.c > create mode 100644 lib/librte_bond/rte_bond.h > > -- > 1.8.5.3 > > This doesn't address any of the comments I made previously. Neil ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-05 11:03 ` Neil Horman @ 2014-06-06 8:23 ` Doherty, Declan 2014-06-06 14:54 ` Neil Horman 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-06 8:23 UTC (permalink / raw) To: dev > -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Thursday, May 29, 2014 12:34 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > On Thu, May 29, 2014 at 10:33:00AM +0000, Doherty, Declan wrote: > > -----Original Message----- > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > Sent: Wednesday, May 28, 2014 6:49 PM > > > To: Doherty, Declan > > > Cc: dev@dpdk.org > > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > > > On Wed, May 28, 2014 at 04:32:00PM +0100, declan.doherty@intel.com > wrote: > > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > > > Initial release of Link Bonding Library (lib/librte_bond) with > > > > support for bonding modes : > > > > 0 - Round Robin > > > > 1 - Active Backup > > > > 2 - Balance l2 / l23 / l34 > > > > 3 - Broadcast > > > > > > > Why make this a separate library? That requires exposure of yet > > > another > API to applications. Instead, why > not write a PMD that can enslave > other PMD's and treat them all as a single interface? That way this > all > > works with the existing API. > > > > > > Neil > > > > Hi Neil, > > the link bonding device is essentially a software PMD, and as such > > supports > all the standard PMD APIs, the only new APIs which the link bonding > library introduces are for the control operations of the bonded > device which are currently unsupported by the standard PMD API. > Operations such as creating, adding/removing slaves, and configuring > the modes of operation of the device have no analogous APIs in the > current PMD API and required new ones to be created . > > Thats really only true in spirit, in the sense that this library > transmits and receives frames like a PMD does. In practice it doesn't > work and isn't architected the same way. You don't register the > driver using the same method as the other PMDs, which means that using > --vdev on the command line wont work for this type of device. It also > implies that applications have to be made specifically aware of the > fact that they are using a bonded interface (i.e. they need to call > the bonding setup routines to create the bond). I would > recommend: > > 1) Register the pmd using the PMD_DRIVER_REGISTER macro, like other > PMD's > 2) Use the kvargs library to support configuration via the --vdev > command line option, so bonds can be created administratively, rather > than just programatically > 3) Separate the command api from the PMD functionality into separate > libraries (use control mbufs to communicate configuration changes to > the pmd). This will allow users to dynamically load the pmd > functionality (without compile or run time linking requirements), and > then optionally use the programatic interface (or not if they want to > save memory) > > Regards > Neil > -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Thursday, June 5, 2014 12:04 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > On Wed, Jun 04, 2014 at 04:18:01PM +0100, declan.doherty@intel.com > wrote: > > From: Declan Doherty <declan.doherty@intel.com> > > > > v2 patch additions, > > fix for tx burst broadcast, incrementing the reference count on each > > mbuf by the number of slaves - 1 add/remove slave behavior chnange > > to fix primary slave port assignment patchcheck code fixes > > > > > > > This doesn't address any of the comments I made previously. > Neil Hi Neil, sorry for not replying regarding this earlier, in relation to your recommendations 1 and 2, I'm currently working on the implementation of both and should have an additional patch within the next couple of days to add this functionality. Regarding your third recommendation, I have no problem splitting the library into separate API and PMD libraries although this does seem to break convention with the other non hw based pmd's, such as pmd_pcap, so would prefer not to go down that route. Also, I don't see the ability to dynamically load the pmd as justification for the use of a control mbufs interface, this would require that either additional queues be created to handle control messages, or that the data TX queues have the ability to identify and process control mbufs, and either of these options will have a performance hit, for the core handling the separate control messages or directly to the tx burst performance. I am not currently aware of any functional requirements or use cases for bonded devices to be used over multiple processes which is the only reason I can see for using an interface based on control mbufs, and by providing a --vdev configuration options the user can still write their applications without any knowledge of the additional link bonding programmatic interface. If in the future a use case does emerge which requires multi-process control of bonded devices, I think it would make sense then to add an additional library to provide this functionality. Regards Declan -------------------------------------------------------------- Intel Shannon Limited Registered in Ireland Registered Office: Collinstown Industrial Park, Leixlip, County Kildare Registered Number: 308263 Business address: Dromore House, East Park, Shannon, Co. Clare This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-06 8:23 ` Doherty, Declan @ 2014-06-06 14:54 ` Neil Horman 2014-06-13 14:56 ` Doherty, Declan 0 siblings, 1 reply; 127+ messages in thread From: Neil Horman @ 2014-06-06 14:54 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev On Fri, Jun 06, 2014 at 08:23:31AM +0000, Doherty, Declan wrote: > > -----Original Message----- > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > Sent: Thursday, May 29, 2014 12:34 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > On Thu, May 29, 2014 at 10:33:00AM +0000, Doherty, Declan wrote: > > > -----Original Message----- > > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > > Sent: Wednesday, May 28, 2014 6:49 PM > > > > To: Doherty, Declan > > > > Cc: dev@dpdk.org > > > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > > > > > On Wed, May 28, 2014 at 04:32:00PM +0100, declan.doherty@intel.com > > wrote: > > > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > > > > > Initial release of Link Bonding Library (lib/librte_bond) with > > > > > support for bonding modes : > > > > > 0 - Round Robin > > > > > 1 - Active Backup > > > > > 2 - Balance l2 / l23 / l34 > > > > > 3 - Broadcast > > > > > > > > > Why make this a separate library? That requires exposure of yet > > > > another > > API to applications. Instead, why > not write a PMD that can enslave > > other PMD's and treat them all as a single interface? That way this > > all > > works with the existing API. > > > > > > > > Neil > > > > > > Hi Neil, > > > the link bonding device is essentially a software PMD, and as such > > > supports > > all the standard PMD APIs, the only new APIs which the link bonding > > library introduces are for the control operations of the bonded > > device which are currently unsupported by the standard PMD API. > > Operations such as creating, adding/removing slaves, and configuring > > the modes of operation of the device have no analogous APIs in the > > current PMD API and required new ones to be created . > > > > Thats really only true in spirit, in the sense that this library > > transmits and receives frames like a PMD does. In practice it doesn't > > work and isn't architected the same way. You don't register the > > driver using the same method as the other PMDs, which means that using > > --vdev on the command line wont work for this type of device. It also > > implies that applications have to be made specifically aware of the > > fact that they are using a bonded interface (i.e. they need to call > > the bonding setup routines to create the bond). I would > > recommend: > > > > 1) Register the pmd using the PMD_DRIVER_REGISTER macro, like other > > PMD's > > 2) Use the kvargs library to support configuration via the --vdev > > command line option, so bonds can be created administratively, rather > > than just programatically > > 3) Separate the command api from the PMD functionality into separate > > libraries (use control mbufs to communicate configuration changes to > > the pmd). This will allow users to dynamically load the pmd > > functionality (without compile or run time linking requirements), and > > then optionally use the programatic interface (or not if they want to > > save memory) > > > > Regards > > Neil > > -----Original Message----- > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > Sent: Thursday, June 5, 2014 12:04 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > > > On Wed, Jun 04, 2014 at 04:18:01PM +0100, declan.doherty@intel.com > > wrote: > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > v2 patch additions, > > > fix for tx burst broadcast, incrementing the reference count on each > > > mbuf by the number of slaves - 1 add/remove slave behavior chnange > > > to fix primary slave port assignment patchcheck code fixes > > > > > > > > > > > This doesn't address any of the comments I made previously. > > Neil > > > Hi Neil, > > sorry for not replying regarding this earlier, in relation to your recommendations 1 and 2, I'm currently working on the implementation of both and should have an additional patch within the next couple of days to add this functionality. > > Regarding your third recommendation, I have no problem splitting the library into separate API and PMD libraries although this does seem to break convention with the other non hw based pmd's, such as pmd_pcap, I'm not at all sure what you mean here. pmd_pcap registers a driver with the registration mechanism, allowing for the use of the --vdev option on the command line, same as all the other virtual drivers. The physical drivers do the same thing using the --whitelist and --blacklist options. How is the bonding driver following convention with pmd_pcap? > so would prefer not to go down that route. Also, I don't see the ability to dynamically load the pmd as justification for the use of a control mbufs interface, this would require that either additional queues be created to handle control messages, or that the data TX queues have the ability to identify and process control mbufs, and either of these options will have a performance hit, Well, you're correct in that the driver would have to identify control messages, but I don't see how thats a performance hit at all. If you create a control channel queue, then all you have to introduce to the datapath is a single branch to see which queue is transmitting, and your done. If control mbufs are just that hurtful to performance, it seems thats an area that really needs improvement then. Another alternative is to add a control packet method to the rte_eth_dev structure allowing you to pass control mbufs directly to a pmd out of line with the datapath. That could avoid any additional branches in the datapath, and still allow you to separate user api from driver implementation. > for the core handling the separate control messages or directly to the tx burst performance. I am not currently aware of any functional requirements or use cases for bonded devices to be used over multiple processes which is the only reason I can see for using an interface based on control mbufs, The reason is administrative. Its got nothing to do with how many processes want to use an interface, it has to do with an interface knowing if its using the bonding driver or not (it shouldn't ever have to, but with your patch set, its required to use the bonding api to create the interface and add slaves. > and by providing a --vdev configuration options the user can still write their applications without any knowledge of the additional link bonding programmatic interface. If in the future a use case does emerge which requires multi-process control of bonded devices, I think it would make sense then to add an additional library to provide this functionality. How exactly does that happen? At what point does the bonding library in its current form register a driver with rte_ethdev without the application first calling some function in the bonding library to do so. Because until that happens, --vdev is non functional for this pmd. FWIW, the concern for me here is one of packaging. I don't want users to have to pull in a library that they don't absolutely need. the way you have this put together right now, they need the entire bonding library even if they just want a statically configured bond interface Neil > > Regards > Declan > -------------------------------------------------------------- > Intel Shannon Limited > Registered in Ireland > Registered Office: Collinstown Industrial Park, Leixlip, County Kildare > Registered Number: 308263 > Business address: Dromore House, East Park, Shannon, Co. Clare > > This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. > > > ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-06 14:54 ` Neil Horman @ 2014-06-13 14:56 ` Doherty, Declan 2014-06-13 15:11 ` Neil Horman 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-13 14:56 UTC (permalink / raw) To: Neil Horman; +Cc: dev > -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Friday, June 6, 2014 3:54 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > On Fri, Jun 06, 2014 at 08:23:31AM +0000, Doherty, Declan wrote: > > > -----Original Message----- > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > Sent: Thursday, May 29, 2014 12:34 PM > > > To: Doherty, Declan > > > Cc: dev@dpdk.org > > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > > > On Thu, May 29, 2014 at 10:33:00AM +0000, Doherty, Declan wrote: > > > > -----Original Message----- > > > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > > > Sent: Wednesday, May 28, 2014 6:49 PM > > > > > To: Doherty, Declan > > > > > Cc: dev@dpdk.org > > > > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > > > > > > > On Wed, May 28, 2014 at 04:32:00PM +0100, declan.doherty@intel.com > > > wrote: > > > > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > > > > > > > Initial release of Link Bonding Library (lib/librte_bond) with > > > > > > support for bonding modes : > > > > > > 0 - Round Robin > > > > > > 1 - Active Backup > > > > > > 2 - Balance l2 / l23 / l34 > > > > > > 3 - Broadcast > > > > > > > > > > > Why make this a separate library? That requires exposure of yet > > > > > another > > > API to applications. Instead, why > not write a PMD that can enslave > > > other PMD's and treat them all as a single interface? That way this > > > all > > works with the existing API. > > > > > > > > > > Neil > > > > > > > > Hi Neil, > > > > the link bonding device is essentially a software PMD, and as such > > > > supports > > > all the standard PMD APIs, the only new APIs which the link bonding > > > library introduces are for the control operations of the bonded > > > device which are currently unsupported by the standard PMD API. > > > Operations such as creating, adding/removing slaves, and configuring > > > the modes of operation of the device have no analogous APIs in the > > > current PMD API and required new ones to be created . > > > > > > Thats really only true in spirit, in the sense that this library > > > transmits and receives frames like a PMD does. In practice it doesn't > > > work and isn't architected the same way. You don't register the > > > driver using the same method as the other PMDs, which means that using > > > --vdev on the command line wont work for this type of device. It also > > > implies that applications have to be made specifically aware of the > > > fact that they are using a bonded interface (i.e. they need to call > > > the bonding setup routines to create the bond). I would > > > recommend: > > > > > > 1) Register the pmd using the PMD_DRIVER_REGISTER macro, like other > > > PMD's > > > 2) Use the kvargs library to support configuration via the --vdev > > > command line option, so bonds can be created administratively, rather > > > than just programatically > > > 3) Separate the command api from the PMD functionality into separate > > > libraries (use control mbufs to communicate configuration changes to > > > the pmd). This will allow users to dynamically load the pmd > > > functionality (without compile or run time linking requirements), and > > > then optionally use the programatic interface (or not if they want to > > > save memory) > > > > > > Regards > > > Neil > > > -----Original Message----- > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > Sent: Thursday, June 5, 2014 12:04 PM > > > To: Doherty, Declan > > > Cc: dev@dpdk.org > > > Subject: Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > > > > > On Wed, Jun 04, 2014 at 04:18:01PM +0100, declan.doherty@intel.com > > > wrote: > > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > > > v2 patch additions, > > > > fix for tx burst broadcast, incrementing the reference count on each > > > > mbuf by the number of slaves - 1 add/remove slave behavior chnange > > > > to fix primary slave port assignment patchcheck code fixes > > > > > > > > > > > > > > > This doesn't address any of the comments I made previously. > > > Neil > > > > > > Hi Neil, > > > > sorry for not replying regarding this earlier, in relation to your > recommendations 1 and 2, I'm currently working on the implementation of both > and should have an additional patch within the next couple of days to add this > functionality. > > > > Regarding your third recommendation, I have no problem splitting the library > into separate API and PMD libraries although this does seem to break convention > with the other non hw based pmd's, such as pmd_pcap, > I'm not at all sure what you mean here. pmd_pcap registers a driver with the > registration mechanism, allowing for the use of the --vdev option on the command > line, same as all the other virtual drivers. The physical drivers do the same > thing using the --whitelist and --blacklist options. How is the bonding driver > following convention with pmd_pcap? > > > > so would prefer not to go down that route. Also, I don't see the ability to > dynamically load the pmd as justification for the use of a control mbufs interface, > this would require that either additional queues be created to handle control > messages, or that the data TX queues have the ability to identify and process > control mbufs, and either of these options will have a performance hit, > > Well, you're correct in that the driver would have to identify control messages, > but I don't see how thats a performance hit at all. If you create a control > channel queue, then all you have to introduce to the datapath is a single branch > to see which queue is transmitting, and your done. If control mbufs are just > that hurtful to performance, it seems thats an area that really needs > improvement then. > > Another alternative is to add a control packet method to the rte_eth_dev > structure allowing you to pass control mbufs directly to a pmd out of line with > the datapath. That could avoid any additional branches in the datapath, and > still allow you to separate user api from driver implementation. > > > > for the core handling the separate control messages or directly to the tx burst > performance. I am not currently aware of any functional requirements or use > cases for bonded devices to be used over multiple processes which is the only > reason I can see for using an interface based on control mbufs, > > The reason is administrative. Its got nothing to do with how many processes > want to use an interface, it has to do with an interface knowing if its using > the bonding driver or not (it shouldn't ever have to, but with your patch set, > its required to use the bonding api to create the interface and add slaves. > > > and by providing a --vdev configuration options the user can still write their > applications without any knowledge of the additional link bonding programmatic > interface. If in the future a use case does emerge which requires multi-process > control of bonded devices, I think it would make sense then to add an additional > library to provide this functionality. > > How exactly does that happen? At what point does the bonding library in its > current form register a driver with rte_ethdev without the application first > calling some function in the bonding library to do so. Because until that > happens, --vdev is non functional for this pmd. > > FWIW, the concern for me here is one of packaging. I don't want users to have > to pull in a library that they don't absolutely need. the way you have this put > together right now, they need the entire bonding library even if they just want > a statically configured bond interface > > Neil > > > > > > Regards > > Declan > > -------------------------------------------------------------- Hi Neil, I waited to reply to this until I had v3 of the patch ready to submit. I think you mistook the intent of my last response, but anyway I think v3 of the patch addresses the majority of your concerns regarding the bonded device. Which can now be initialized using the --vdev options and supports both virtual and physical devices to be specified as slaves and no knowledge of the bonding API is required by the user application if the bonding ports are configure at runtime. Regards ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-13 14:56 ` Doherty, Declan @ 2014-06-13 15:11 ` Neil Horman 0 siblings, 0 replies; 127+ messages in thread From: Neil Horman @ 2014-06-13 15:11 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev On Fri, Jun 13, 2014 at 02:56:47PM +0000, Doherty, Declan wrote: > > -----Original Message----- > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > Sent: Friday, June 6, 2014 3:54 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > > > On Fri, Jun 06, 2014 at 08:23:31AM +0000, Doherty, Declan wrote: > > > > -----Original Message----- > > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > > Sent: Thursday, May 29, 2014 12:34 PM > > > > To: Doherty, Declan > > > > Cc: dev@dpdk.org > > > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > > > > > On Thu, May 29, 2014 at 10:33:00AM +0000, Doherty, Declan wrote: > > > > > -----Original Message----- > > > > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > > > > Sent: Wednesday, May 28, 2014 6:49 PM > > > > > > To: Doherty, Declan > > > > > > Cc: dev@dpdk.org > > > > > > Subject: Re: [dpdk-dev] [PATCH 0/4] Link Bonding Library > > > > > > > > > > > > On Wed, May 28, 2014 at 04:32:00PM +0100, declan.doherty@intel.com > > > > wrote: > > > > > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > > > > > > > > > Initial release of Link Bonding Library (lib/librte_bond) with > > > > > > > support for bonding modes : > > > > > > > 0 - Round Robin > > > > > > > 1 - Active Backup > > > > > > > 2 - Balance l2 / l23 / l34 > > > > > > > 3 - Broadcast > > > > > > > > > > > > > Why make this a separate library? That requires exposure of yet > > > > > > another > > > > API to applications. Instead, why > not write a PMD that can enslave > > > > other PMD's and treat them all as a single interface? That way this > > > > all > > works with the existing API. > > > > > > > > > > > > Neil > > > > > > > > > > Hi Neil, > > > > > the link bonding device is essentially a software PMD, and as such > > > > > supports > > > > all the standard PMD APIs, the only new APIs which the link bonding > > > > library introduces are for the control operations of the bonded > > > > device which are currently unsupported by the standard PMD API. > > > > Operations such as creating, adding/removing slaves, and configuring > > > > the modes of operation of the device have no analogous APIs in the > > > > current PMD API and required new ones to be created . > > > > > > > > Thats really only true in spirit, in the sense that this library > > > > transmits and receives frames like a PMD does. In practice it doesn't > > > > work and isn't architected the same way. You don't register the > > > > driver using the same method as the other PMDs, which means that using > > > > --vdev on the command line wont work for this type of device. It also > > > > implies that applications have to be made specifically aware of the > > > > fact that they are using a bonded interface (i.e. they need to call > > > > the bonding setup routines to create the bond). I would > > > > recommend: > > > > > > > > 1) Register the pmd using the PMD_DRIVER_REGISTER macro, like other > > > > PMD's > > > > 2) Use the kvargs library to support configuration via the --vdev > > > > command line option, so bonds can be created administratively, rather > > > > than just programatically > > > > 3) Separate the command api from the PMD functionality into separate > > > > libraries (use control mbufs to communicate configuration changes to > > > > the pmd). This will allow users to dynamically load the pmd > > > > functionality (without compile or run time linking requirements), and > > > > then optionally use the programatic interface (or not if they want to > > > > save memory) > > > > > > > > Regards > > > > Neil > > > > -----Original Message----- > > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > > Sent: Thursday, June 5, 2014 12:04 PM > > > > To: Doherty, Declan > > > > Cc: dev@dpdk.org > > > > Subject: Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > > > > > > > On Wed, Jun 04, 2014 at 04:18:01PM +0100, declan.doherty@intel.com > > > > wrote: > > > > > From: Declan Doherty <declan.doherty@intel.com> > > > > > > > > > > v2 patch additions, > > > > > fix for tx burst broadcast, incrementing the reference count on each > > > > > mbuf by the number of slaves - 1 add/remove slave behavior chnange > > > > > to fix primary slave port assignment patchcheck code fixes > > > > > > > > > > > > > > > > > > > This doesn't address any of the comments I made previously. > > > > Neil > > > > > > > > > Hi Neil, > > > > > > sorry for not replying regarding this earlier, in relation to your > > recommendations 1 and 2, I'm currently working on the implementation of both > > and should have an additional patch within the next couple of days to add this > > functionality. > > > > > > Regarding your third recommendation, I have no problem splitting the library > > into separate API and PMD libraries although this does seem to break convention > > with the other non hw based pmd's, such as pmd_pcap, > > I'm not at all sure what you mean here. pmd_pcap registers a driver with the > > registration mechanism, allowing for the use of the --vdev option on the command > > line, same as all the other virtual drivers. The physical drivers do the same > > thing using the --whitelist and --blacklist options. How is the bonding driver > > following convention with pmd_pcap? > > > > > > > so would prefer not to go down that route. Also, I don't see the ability to > > dynamically load the pmd as justification for the use of a control mbufs interface, > > this would require that either additional queues be created to handle control > > messages, or that the data TX queues have the ability to identify and process > > control mbufs, and either of these options will have a performance hit, > > > > Well, you're correct in that the driver would have to identify control messages, > > but I don't see how thats a performance hit at all. If you create a control > > channel queue, then all you have to introduce to the datapath is a single branch > > to see which queue is transmitting, and your done. If control mbufs are just > > that hurtful to performance, it seems thats an area that really needs > > improvement then. > > > > Another alternative is to add a control packet method to the rte_eth_dev > > structure allowing you to pass control mbufs directly to a pmd out of line with > > the datapath. That could avoid any additional branches in the datapath, and > > still allow you to separate user api from driver implementation. > > > > > > > for the core handling the separate control messages or directly to the tx burst > > performance. I am not currently aware of any functional requirements or use > > cases for bonded devices to be used over multiple processes which is the only > > reason I can see for using an interface based on control mbufs, > > > > The reason is administrative. Its got nothing to do with how many processes > > want to use an interface, it has to do with an interface knowing if its using > > the bonding driver or not (it shouldn't ever have to, but with your patch set, > > its required to use the bonding api to create the interface and add slaves. > > > > > and by providing a --vdev configuration options the user can still write their > > applications without any knowledge of the additional link bonding programmatic > > interface. If in the future a use case does emerge which requires multi-process > > control of bonded devices, I think it would make sense then to add an additional > > library to provide this functionality. > > > > How exactly does that happen? At what point does the bonding library in its > > current form register a driver with rte_ethdev without the application first > > calling some function in the bonding library to do so. Because until that > > happens, --vdev is non functional for this pmd. > > > > FWIW, the concern for me here is one of packaging. I don't want users to have > > to pull in a library that they don't absolutely need. the way you have this put > > together right now, they need the entire bonding library even if they just want > > a statically configured bond interface > > > > Neil > > > > > > > > > > Regards > > > Declan > > > -------------------------------------------------------------- > > Hi Neil, > I waited to reply to this until I had v3 of the patch ready to submit. I think you mistook the intent of my last > response, but anyway I think v3 of the patch addresses the majority of your concerns regarding the bonded device. Which > can now be initialized using the --vdev options and supports both virtual and physical devices to be specified > as slaves and no knowledge of the bonding API is required by the user application if the bonding ports are configure at > runtime. > Sounds great, I look forward to seeing it. Thanks! Neil > Regards > ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty ` (7 preceding siblings ...) 2014-06-05 11:03 ` Neil Horman @ 2014-06-06 3:26 ` Cao, Waterman 2014-06-11 16:33 ` Thomas Monjalon 9 siblings, 0 replies; 127+ messages in thread From: Cao, Waterman @ 2014-06-06 3:26 UTC (permalink / raw) To: Doherty, Declan, dev Tested-by: Waterman Cao <waterman.cao@intel.com> This patch is updated version for Link Bonding library. It's compose of 5 files including cover letter, tested by Intel. Please see environment information: Fedora 20 x86_64, Linux Kernel 3.11.10-301, GCC 4.8.2 Intel Xeon CPU E5-2680 v2 @ 2.80GHz NIC: Intel Niantic 82599 Total cases Passed Failed 18 18 0 Please see test Case list as the following: Case1: Basic bonding--Create bonded devices and slaves Case2: Basic bonding--MAC Address Test Case3: Basic bonding--Device Promiscuous Mode Test Case4: Mode 0(Round Robin) TX/RX test Case5: Mode 0(Round Robin) Bring one slave link down Case6: Mode 0(Round Robin) Bring all slave links down Case7: Mode 1(Active Backup) TX/RX Test Case8: Mode 1(Active Backup) Change active slave, RX/TX test Case9: Mode 1(Active Backup) Link up/down active eth dev Case10: Mode 1(Active Backup) Bring all slave links down Case11: Mode 2(Balance XOR) TX Load Balance test Case12: Mode 2(Balance XOR) TX Load Balance Link down Case13: Mode 2(Balance XOR) Bring all slave links down Case14: Mode 2(Balance XOR) Layer 3+4 forwarding Case15: Mode 2(Balance XOR) RX test Case16: Mode 3(Broadcast) TX/RX Test Case17: Mode 3(Broadcast) Bring one slave link down Case18: Mode 3(Broadcast) Bring all slave links down ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty ` (8 preceding siblings ...) 2014-06-06 3:26 ` Cao, Waterman @ 2014-06-11 16:33 ` Thomas Monjalon 2014-06-13 14:08 ` Doherty, Declan 9 siblings, 1 reply; 127+ messages in thread From: Thomas Monjalon @ 2014-06-11 16:33 UTC (permalink / raw) To: declan.doherty; +Cc: dev Hi Declan, Do you think you can send another version of your serie soon? Some comments need to be addressed. 2 formatting comments: - I saw some strange alignments of comments - you should send your patches as children of the cover letter Thanks -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-11 16:33 ` Thomas Monjalon @ 2014-06-13 14:08 ` Doherty, Declan 2014-06-13 15:15 ` Thomas Monjalon 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-13 14:08 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev > -----Original Message----- > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > Sent: Wednesday, June 11, 2014 5:33 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library > > Hi Declan, > > Do you think you can send another version of your serie soon? > Some comments need to be addressed. > > 2 formatting comments: > - I saw some strange alignments of comments > - you should send your patches as children of the cover letter > > Thanks > -- > Thomas Hi Thomas, I've just submitted v3 of the patch, which should address the majority of the comments and any formatting issues. Regards Declan ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v2 0/4] Link Bonding Library 2014-06-13 14:08 ` Doherty, Declan @ 2014-06-13 15:15 ` Thomas Monjalon 0 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-13 15:15 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev, Neil Horman 2014-06-13 14:08, Doherty, Declan: > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > > Do you think you can send another version of your serie soon? > > Some comments need to be addressed. > > > > 2 formatting comments: > > - I saw some strange alignments of comments > > - you should send your patches as children of the cover letter > > Hi Thomas, > I've just submitted v3 of the patch, which should address the majority of the comments and any formatting issues. Thanks. We are now waiting for Neil's acknowledgement. On the formatting side, please check these errors: ERROR: "foo* bar" should be "foo *bar" ERROR: space prohibited before that close parenthesis ')' About git-send-email, I'd prefer you conform to this scheme for man page: " So for example when --thread and --no-chain-reply-to are specified, the second and subsequent patches will be replies to the first one like in the illustration below where [PATCH v2 0/3] is in reply to [PATCH 0/2]: [PATCH 0/2] Here is what I did... [PATCH 1/2] Clean up and tests [PATCH 2/2] Implementation [PATCH v2 0/3] Here is a reroll [PATCH v2 1/3] Clean up [PATCH v2 2/3] New tests [PATCH v2 3/3] Implementation " Thanks -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty ` (6 preceding siblings ...) 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty @ 2014-06-13 14:41 ` Declan Doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 1/5] " Declan Doherty ` (12 more replies) 7 siblings, 13 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-13 14:41 UTC (permalink / raw) To: dev, dev This patch contains the initial release of the Link Bonding PMD Library Supporting bonding modes: 0 - Round Robin 1 - Active Backup 2 - Balance (Supporting 3 transmission polices) layer 2, layer 2+3, layer 3+4 3 - Broadcast Version 3 of this patch set add the following functionality changes - Link bonding command line option parsing / initialization support - Unique name identifier to rte_eth_dev_data struct to identify virtual ethdev's which are to be added to bondded device from command line. - Updates to EAL to support initialization of link bonding devices Patch Set Description: 0001 - librte_pmd_bond + makefile changes 0002 - librte_eal / librte_ether changes to support bonding device intialization 0003 - link bonding unti test suite 0004 - testpmd link bonding support changes 0005 - doxygen additions app/test-pmd/cmdline.c | 571 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 37 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 288 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/common/eal_common_dev.c | 66 +- lib/librte_eal/common/eal_common_pci.c | 6 + lib/librte_eal/common/include/eal_private.h | 7 + lib/librte_eal/common/include/rte_dev.h | 1 + lib/librte_ether/rte_ethdev.c | 34 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2149 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 5 + 32 files changed, 8192 insertions(+), 43 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v3 1/5] Link Bonding PMD Library 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty @ 2014-06-13 14:41 ` Declan Doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) Declan Doherty ` (11 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-13 14:41 UTC (permalink / raw) To: dev, dev Link Bonding Library (lib/librte_pmd_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2149 ++++++++++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 +++++ mk/rte.app.mk | 5 + 7 files changed, 2452 insertions(+) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index ec7b159..545d760 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -187,6 +187,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 7c143eb..eb2e3d0 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -216,6 +216,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/lib/Makefile b/lib/Makefile index 8cdfa7d..66232a9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -47,6 +47,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_NET) += librte_net diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..9275830 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,32 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.c b/lib/librte_pmd_bond/rte_eth_bond.c new file mode 100644 index 0000000..01d917d --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.c @@ -0,0 +1,2149 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" + +/* Link Bonding KVARG option defintions for --vdev */ +#define PMD_BOND_KVARG ("eth_bond") + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +static const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int last_slave_idx = -1; + int i, slave_idx; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + slave_idx = i % num_of_slaves; + slave_bufs[slave_idx][(slave_nb_pkts[slave_idx])++] = bufs[i]; + } + + /* calculate the next slave to transmit on based on the last slave idx used + * in the last call to bond_ethdev_tx_burst_round_robin */ + slave_idx = last_slave_idx + 1; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + slave_idx = (slave_idx + i) % num_of_slaves; + + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[slave_idx], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + } + } + + last_slave_idx = slave_idx; + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + + + +static void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + + return port_id; +} +static int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +static int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +static int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +static int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +static int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +static int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} + + +static int +rte_pmd_bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver rte_pmd_bond_drv = { + .name = PMD_BOND_KVARG, + .type = PMD_BDEV, + .init = rte_pmd_bond_init, +}; + +PMD_REGISTER_DRIVER(rte_pmd_bond_drv); + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} + diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..7cf9dd8 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 914c568..66de6a8 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -181,8 +181,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 1/5] " Declan Doherty @ 2014-06-13 14:41 ` Declan Doherty 2014-06-13 16:08 ` Neil Horman 2014-06-13 21:59 ` Stephen Hemminger 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 3/5] Link Bonding PMD Library (Unit Test Suite) Declan Doherty ` (10 subsequent siblings) 12 siblings, 2 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-13 14:41 UTC (permalink / raw) To: dev, dev [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain, Size: 15528 bytes --] Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_eal/common/eal_common_dev.c | 66 +++++++++++++++++++++++++++-- lib/librte_eal/common/eal_common_pci.c | 6 +++ lib/librte_eal/common/include/eal_private.h | 7 +++ lib/librte_eal/common/include/rte_dev.h | 1 + lib/librte_ether/rte_ethdev.c | 34 +++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 ++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +++++----- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 10 files changed, 144 insertions(+), 36 deletions(-) diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..b50c908 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -75,14 +75,28 @@ rte_eal_dev_init(void) /* call the init function for each virtual device */ TAILQ_FOREACH(devargs, &devargs_list, next) { + uint8_t bdev = 0; if (devargs->type != RTE_DEVTYPE_VIRTUAL) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device*/ + if (driver->type != PMD_VDEV && driver->type != PMD_BDEV) continue; + /* + * Bonded devices are not initialize here, we do it later in + * rte_eal_bonded_dev_init() after all physical devices have been + * probed and initialized + */ + if (driver->type == PMD_BDEV && + !strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + bdev = 1; + break; + } + /* search a driver prefix in virtual device name */ if (!strncmp(driver->name, devargs->virtual.drv_name, strlen(driver->name))) { @@ -92,9 +106,9 @@ rte_eal_dev_init(void) } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + if (driver == NULL && !bdev) { + rte_panic("no driver found for %s and is not a bonded vdev %d\n", + devargs->virtual.drv_name, bdev); } } @@ -107,3 +121,47 @@ rte_eal_dev_init(void) } return 0; } + +#ifdef RTE_LIBRTE_PMD_BOND +int +rte_eal_bonded_dev_init(void) +{ + struct rte_devargs *devargs; + struct rte_driver *driver; + + TAILQ_FOREACH(devargs, &devargs_list, next) { + int vdev = 0; + + if (devargs->type != RTE_DEVTYPE_VIRTUAL) + continue; + + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_VDEV && driver->type != PMD_BDEV) + continue; + + /* Virtual devices have already been initialized so we skip them + * here*/ + if (driver->type == PMD_VDEV && + !strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + vdev = 1; + break; + } + + /* search a driver prefix in bonded device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + driver->init(devargs->virtual.drv_name, devargs->args); + break; + } + } + + if (driver == NULL && !vdev) { + rte_panic("no driver found for %s\n", + devargs->virtual.drv_name); + } + } + return 0; +} +#endif + diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index 4d877ea..9b584f5 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -166,7 +166,13 @@ rte_eal_pci_probe(void) dev->addr.devid, dev->addr.function); } +#ifdef RTE_LIBRTE_PMD_BOND + /* After all physical PCI devices have been probed and initialized then we + * initialize the bonded devices */ + return rte_eal_bonded_dev_init(); +#else return 0; +#endif } /* dump one device */ diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..f6081bb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -203,4 +203,11 @@ int rte_eal_alarm_init(void); */ int rte_eal_dev_init(void); +#ifdef RTE_LIBRTE_PMD_BOND +/** + * Initialize the bonded devices + */ +int rte_eal_bonded_dev_init(void); +#endif + #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..f0a780a 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,7 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ }; /** diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 8011b8b..4c2f1d3 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -64,6 +64,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -152,8 +153,21 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static int +rte_eth_dev_name_unique(const char* name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return -1; + } + + return 0; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char* name) { struct rte_eth_dev *eth_dev; @@ -165,23 +179,37 @@ rte_eth_dev_allocate(void) if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_name_unique(name)) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + rte_snprintf(eth_dev->data->name , sizeof(eth_dev->data->name ), + "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } static int rte_eth_dev_init(struct rte_pci_driver *pci_drv, - struct rte_pci_device *pci_dev) + struct rte_pci_device *pci_dev) { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique ethdev name by concatenating drive name and number of + * ports */ + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 67eda50..27ed0ab 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1233,6 +1233,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1241,6 +1243,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1293,10 +1297,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..cd6c0e1 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char* name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index 10d4e24..e6779c4 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char* name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - rte_snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + rte_snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - rte_snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + rte_snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node) ) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 7c4d3fe..a0b4bdd 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) Declan Doherty @ 2014-06-13 16:08 ` Neil Horman 2014-06-13 18:34 ` Doherty, Declan 2014-06-13 21:59 ` Stephen Hemminger 1 sibling, 1 reply; 127+ messages in thread From: Neil Horman @ 2014-06-13 16:08 UTC (permalink / raw) To: Declan Doherty; +Cc: dev On Fri, Jun 13, 2014 at 03:41:59PM +0100, Declan Doherty wrote: > Updating functionality in EAL to support adding link bonding > devices via –vdev option. Link bonding devices will be > initialized after all physical devices have been probed and > initialized. > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > --- > lib/librte_eal/common/eal_common_dev.c | 66 +++++++++++++++++++++++++++-- > lib/librte_eal/common/eal_common_pci.c | 6 +++ > lib/librte_eal/common/include/eal_private.h | 7 +++ > lib/librte_eal/common/include/rte_dev.h | 1 + > lib/librte_ether/rte_ethdev.c | 34 +++++++++++++-- > lib/librte_ether/rte_ethdev.h | 7 ++- > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +++++----- > lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++------- > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > 10 files changed, 144 insertions(+), 36 deletions(-) > > diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c > index eae5656..b50c908 100644 > --- a/lib/librte_eal/common/eal_common_dev.c > +++ b/lib/librte_eal/common/eal_common_dev.c > @@ -75,14 +75,28 @@ rte_eal_dev_init(void) > > /* call the init function for each virtual device */ > TAILQ_FOREACH(devargs, &devargs_list, next) { > + uint8_t bdev = 0; > > if (devargs->type != RTE_DEVTYPE_VIRTUAL) > continue; > > TAILQ_FOREACH(driver, &dev_driver_list, next) { > - if (driver->type != PMD_VDEV) > + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device*/ > + if (driver->type != PMD_VDEV && driver->type != PMD_BDEV) > continue; > > + /* > + * Bonded devices are not initialize here, we do it later in > + * rte_eal_bonded_dev_init() after all physical devices have been > + * probed and initialized > + */ > + if (driver->type == PMD_BDEV && > + !strncmp(driver->name, devargs->virtual.drv_name, > + strlen(driver->name))) { > + bdev = 1; > + break; > + } > + I really don't think you need to add a new device type for bonded devs. Its got no specific hardware that it drives, and you configure it with a --vdev command, so treat it as one here. I understand that you need to pass additional information about slaves to a bonded device, which is fine, but you can do that with kvargs pretty easily, at which point its just another vdev. The only other requirement is that you initilize the bonded vdev after the slave vdevs have been created, which you can do by any of several methods (a priority field to indicate that bonded drivers should be initilized last/later, a deferral return code from the init routine, or by dead reckoning via the careful construction of the application command line (placed the bonded --vdev option last on the command line argument list at run time). > /* search a driver prefix in virtual device name */ > if (!strncmp(driver->name, devargs->virtual.drv_name, > strlen(driver->name))) { > @@ -92,9 +106,9 @@ rte_eal_dev_init(void) > } > } > > - if (driver == NULL) { > - rte_panic("no driver found for %s\n", > - devargs->virtual.drv_name); > + if (driver == NULL && !bdev) { > + rte_panic("no driver found for %s and is not a bonded vdev %d\n", > + devargs->virtual.drv_name, bdev); > } > } > > @@ -107,3 +121,47 @@ rte_eal_dev_init(void) > } > return 0; > } > + > +#ifdef RTE_LIBRTE_PMD_BOND > +int > +rte_eal_bonded_dev_init(void) > +{ > + struct rte_devargs *devargs; > + struct rte_driver *driver; > + > + TAILQ_FOREACH(devargs, &devargs_list, next) { > + int vdev = 0; > + > + if (devargs->type != RTE_DEVTYPE_VIRTUAL) > + continue; > + > + TAILQ_FOREACH(driver, &dev_driver_list, next) { > + if (driver->type != PMD_VDEV && driver->type != PMD_BDEV) > + continue; > + > + /* Virtual devices have already been initialized so we skip them > + * here*/ > + if (driver->type == PMD_VDEV && > + !strncmp(driver->name, devargs->virtual.drv_name, > + strlen(driver->name))) { > + vdev = 1; > + break; > + } > + > + /* search a driver prefix in bonded device name */ > + if (!strncmp(driver->name, devargs->virtual.drv_name, > + strlen(driver->name))) { > + driver->init(devargs->virtual.drv_name, devargs->args); > + break; > + } > + } > + > + if (driver == NULL && !vdev) { > + rte_panic("no driver found for %s\n", > + devargs->virtual.drv_name); > + } > + } > + return 0; > +} > +#endif > + If you treat bonded devices as vdevs, you can remove this function entirely. > diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c > index 4d877ea..9b584f5 100644 > --- a/lib/librte_eal/common/eal_common_pci.c > +++ b/lib/librte_eal/common/eal_common_pci.c > @@ -166,7 +166,13 @@ rte_eal_pci_probe(void) > dev->addr.devid, dev->addr.function); > } > > +#ifdef RTE_LIBRTE_PMD_BOND > + /* After all physical PCI devices have been probed and initialized then we > + * initialize the bonded devices */ > + return rte_eal_bonded_dev_init(); > +#else This is the wrong place for this, bonded devices are not pci devices, this doesn't belong in the pci device probe path. If you treat the bonded devices as vdevs and handle the ordering as described above, you won't need this anyway. > return 0; > +#endif > } > > /* dump one device */ > diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h > index 232fcec..f6081bb 100644 > --- a/lib/librte_eal/common/include/eal_private.h > +++ b/lib/librte_eal/common/include/eal_private.h > @@ -203,4 +203,11 @@ int rte_eal_alarm_init(void); > */ > int rte_eal_dev_init(void); > > +#ifdef RTE_LIBRTE_PMD_BOND > +/** > + * Initialize the bonded devices > + */ > +int rte_eal_bonded_dev_init(void); > +#endif > + > #endif /* _EAL_PRIVATE_H_ */ > diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h > index f7e3a10..f0a780a 100644 > --- a/lib/librte_eal/common/include/rte_dev.h > +++ b/lib/librte_eal/common/include/rte_dev.h > @@ -62,6 +62,7 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); > enum pmd_type { > PMD_VDEV = 0, > PMD_PDEV = 1, > + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ > }; Can drop this as noted above. > > /** > diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c > index 8011b8b..4c2f1d3 100644 > --- a/lib/librte_ether/rte_ethdev.c > +++ b/lib/librte_ether/rte_ethdev.c > @@ -64,6 +64,7 @@ > #include <rte_mbuf.h> > #include <rte_errno.h> > #include <rte_spinlock.h> > +#include <rte_string_fns.h> > > #include "rte_ether.h" > #include "rte_ethdev.h" > @@ -152,8 +153,21 @@ rte_eth_dev_data_alloc(void) > RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); > } > > +static int > +rte_eth_dev_name_unique(const char* name) > +{ > + unsigned i; > + > + for (i = 0; i < nb_ports; i++) { > + if (strcmp(rte_eth_devices[i].data->name, name) == 0) > + return -1; > + } > + > + return 0; > +} > + > struct rte_eth_dev * > -rte_eth_dev_allocate(void) > +rte_eth_dev_allocate(const char* name) > { > struct rte_eth_dev *eth_dev; > > @@ -165,23 +179,37 @@ rte_eth_dev_allocate(void) > if (rte_eth_dev_data == NULL) > rte_eth_dev_data_alloc(); > > + if (rte_eth_dev_name_unique(name)) { > + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); > + return NULL; > + } > + This seems fairly racy if you allow dynamic device creation at run time from the application, if multiple threads attempt to create bonds in parallel. > eth_dev = &rte_eth_devices[nb_ports]; > eth_dev->data = &rte_eth_dev_data[nb_ports]; > + rte_snprintf(eth_dev->data->name , sizeof(eth_dev->data->name ), > + "%s", name); > eth_dev->data->port_id = nb_ports++; > return eth_dev; > } > > static int > rte_eth_dev_init(struct rte_pci_driver *pci_drv, > - struct rte_pci_device *pci_dev) > + struct rte_pci_device *pci_dev) > { > struct eth_driver *eth_drv; > struct rte_eth_dev *eth_dev; > + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; > + > int diag; > > eth_drv = (struct eth_driver *)pci_drv; > > - eth_dev = rte_eth_dev_allocate(); > + /* Create unique ethdev name by concatenating drive name and number of > + * ports */ > + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", > + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); > + > + eth_dev = rte_eth_dev_allocate(ethdev_name); > if (eth_dev == NULL) > return -ENOMEM; > > diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h > index 67eda50..27ed0ab 100644 > --- a/lib/librte_ether/rte_ethdev.h > +++ b/lib/librte_ether/rte_ethdev.h > @@ -1233,6 +1233,8 @@ struct rte_eth_dev_sriov { > }; > #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) > > +#define RTE_ETH_NAME_MAX_LEN (32) > + > /** > * @internal > * The data part, with no function pointers, associated with each ethernet device. > @@ -1241,6 +1243,8 @@ struct rte_eth_dev_sriov { > * processes in a multi-process configuration. > */ > struct rte_eth_dev_data { > + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ > + > void **rx_queues; /**< Array of pointers to RX queues. */ > void **tx_queues; /**< Array of pointers to TX queues. */ > uint16_t nb_rx_queues; /**< Number of RX queues. */ > @@ -1293,10 +1297,11 @@ extern uint8_t rte_eth_dev_count(void); > * Allocates a new ethdev slot for an ethernet device and returns the pointer > * to that slot for the driver to use. > * > + * @param name Unique identifier name for each Ethernet device > * @return > * - Slot in the rte_dev_devices array for a new device; > */ > -struct rte_eth_dev *rte_eth_dev_allocate(void); > +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); > > struct eth_driver; > /** > diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c Hmm, we're modifying other pmds for the naming feature, I think it would be best split out into a separate patch. Something entitled "support unique interface naming for virtual pmds" or something. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-13 16:08 ` Neil Horman @ 2014-06-13 18:34 ` Doherty, Declan 2014-06-13 19:38 ` Neil Horman 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-13 18:34 UTC (permalink / raw) To: Neil Horman; +Cc: dev > -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Friday, June 13, 2014 5:08 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > (librte_eal/librte_ether link bonding support changes) > > On Fri, Jun 13, 2014 at 03:41:59PM +0100, Declan Doherty wrote: > > Updating functionality in EAL to support adding link bonding > > devices via –vdev option. Link bonding devices will be > > initialized after all physical devices have been probed and > > initialized. > > > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > > --- > > lib/librte_eal/common/eal_common_dev.c | 66 > +++++++++++++++++++++++++++-- > > lib/librte_eal/common/eal_common_pci.c | 6 +++ > > lib/librte_eal/common/include/eal_private.h | 7 +++ > > lib/librte_eal/common/include/rte_dev.h | 1 + > > lib/librte_ether/rte_ethdev.c | 34 +++++++++++++-- > > lib/librte_ether/rte_ethdev.h | 7 ++- > > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +++++----- > > lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++------- > > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > > 10 files changed, 144 insertions(+), 36 deletions(-) > > > > diff --git a/lib/librte_eal/common/eal_common_dev.c > b/lib/librte_eal/common/eal_common_dev.c > > index eae5656..b50c908 100644 > > --- a/lib/librte_eal/common/eal_common_dev.c > > +++ b/lib/librte_eal/common/eal_common_dev.c > > @@ -75,14 +75,28 @@ rte_eal_dev_init(void) > > > > /* call the init function for each virtual device */ > > TAILQ_FOREACH(devargs, &devargs_list, next) { > > + uint8_t bdev = 0; > > > > if (devargs->type != RTE_DEVTYPE_VIRTUAL) > > continue; > > > > TAILQ_FOREACH(driver, &dev_driver_list, next) { > > - if (driver->type != PMD_VDEV) > > + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded > device*/ > > + if (driver->type != PMD_VDEV && driver->type != > PMD_BDEV) > > continue; > > > > + /* > > + * Bonded devices are not initialize here, we do it later in > > + * rte_eal_bonded_dev_init() after all physical devices > have been > > + * probed and initialized > > + */ > > + if (driver->type == PMD_BDEV && > > + !strncmp(driver->name, devargs- > >virtual.drv_name, > > + strlen(driver->name))) { > > + bdev = 1; > > + break; > > + } > > + > I really don't think you need to add a new device type for bonded devs. Its got > no specific hardware that it drives, and you configure it with a --vdev command, > so treat it as one here. I understand that you need to pass additional > information about slaves to a bonded device, which is fine, but you can do that > with kvargs pretty easily, at which point its just another vdev. The only other > requirement is that you initilize the bonded vdev after the slave vdevs have > been created, which you can do by any of several methods (a priority field to > indicate that bonded drivers should be initilized last/later, a deferral return > code from the init routine, or by dead reckoning via the careful construction of > the application command line (placed the bonded --vdev option last on the > command line argument list at run time). > It was my initial intent to do as you have describe above, but the physical devices cause a real issue here, physical devices don't call through to rte_eth_dev_allocate until during rte_eal_pci_probe call, so it's not possible to initialize the bonded device from within rte_eal_dev_init as the physical devices have not been fully initialized at this point, as a port_id has not been allocated and can't be added as bonding slaves. I don't see away around this without changing the EAL API, which I've tried to avoid with this solution. Ordering isn't an issue, and can easily be solved if the above problem didn't exist, and although a new device type isn't technically required, I think it's a cleaner solution than doing string comparisons. > > > /* search a driver prefix in virtual device name */ > > if (!strncmp(driver->name, devargs->virtual.drv_name, > > strlen(driver->name))) { > > @@ -92,9 +106,9 @@ rte_eal_dev_init(void) > > } > > } > > > > - if (driver == NULL) { > > - rte_panic("no driver found for %s\n", > > - devargs->virtual.drv_name); > > + if (driver == NULL && !bdev) { > > + rte_panic("no driver found for %s and is not a bonded > vdev %d\n", > > + devargs->virtual.drv_name, bdev); > > } > > } > > > > @@ -107,3 +121,47 @@ rte_eal_dev_init(void) > > } > > return 0; > > } > > + > > +#ifdef RTE_LIBRTE_PMD_BOND > > +int > > +rte_eal_bonded_dev_init(void) > > +{ > > + struct rte_devargs *devargs; > > + struct rte_driver *driver; > > + > > + TAILQ_FOREACH(devargs, &devargs_list, next) { > > + int vdev = 0; > > + > > + if (devargs->type != RTE_DEVTYPE_VIRTUAL) > > + continue; > > + > > + TAILQ_FOREACH(driver, &dev_driver_list, next) { > > + if (driver->type != PMD_VDEV && driver->type != > PMD_BDEV) > > + continue; > > + > > + /* Virtual devices have already been initialized so we skip > them > > + * here*/ > > + if (driver->type == PMD_VDEV && > > + !strncmp(driver->name, devargs- > >virtual.drv_name, > > + strlen(driver->name))) { > > + vdev = 1; > > + break; > > + } > > + > > + /* search a driver prefix in bonded device name */ > > + if (!strncmp(driver->name, devargs->virtual.drv_name, > > + strlen(driver->name))) { > > + driver->init(devargs->virtual.drv_name, devargs- > >args); > > + break; > > + } > > + } > > + > > + if (driver == NULL && !vdev) { > > + rte_panic("no driver found for %s\n", > > + devargs->virtual.drv_name); > > + } > > + } > > + return 0; > > +} > > +#endif > > + > If you treat bonded devices as vdevs, you can remove this function entirely. Agreed but only if there is a solution to the issues described above. > > > diff --git a/lib/librte_eal/common/eal_common_pci.c > b/lib/librte_eal/common/eal_common_pci.c > > index 4d877ea..9b584f5 100644 > > --- a/lib/librte_eal/common/eal_common_pci.c > > +++ b/lib/librte_eal/common/eal_common_pci.c > > @@ -166,7 +166,13 @@ rte_eal_pci_probe(void) > > dev->addr.devid, dev->addr.function); > > } > > > > +#ifdef RTE_LIBRTE_PMD_BOND > > + /* After all physical PCI devices have been probed and initialized then we > > + * initialize the bonded devices */ > > + return rte_eal_bonded_dev_init(); > > +#else > This is the wrong place for this, bonded devices are not pci devices, this > doesn't belong in the pci device probe path. If you treat the bonded devices as > vdevs and handle the ordering as described above, you won't need this anyway. See first comment, but unless a new API is added for initialization of the bonded devices, then some sort signal/callback is required here to notify that it is now safe to initialize the bonded devices. > > > return 0; > > +#endif > > } > > > > /* dump one device */ > > diff --git a/lib/librte_eal/common/include/eal_private.h > b/lib/librte_eal/common/include/eal_private.h > > index 232fcec..f6081bb 100644 > > --- a/lib/librte_eal/common/include/eal_private.h > > +++ b/lib/librte_eal/common/include/eal_private.h > > @@ -203,4 +203,11 @@ int rte_eal_alarm_init(void); > > */ > > int rte_eal_dev_init(void); > > > > +#ifdef RTE_LIBRTE_PMD_BOND > > +/** > > + * Initialize the bonded devices > > + */ > > +int rte_eal_bonded_dev_init(void); > > +#endif > > + > > #endif /* _EAL_PRIVATE_H_ */ > > diff --git a/lib/librte_eal/common/include/rte_dev.h > b/lib/librte_eal/common/include/rte_dev.h > > index f7e3a10..f0a780a 100644 > > --- a/lib/librte_eal/common/include/rte_dev.h > > +++ b/lib/librte_eal/common/include/rte_dev.h > > @@ -62,6 +62,7 @@ typedef int (rte_dev_init_t)(const char *name, const char > *args); > > enum pmd_type { > > PMD_VDEV = 0, > > PMD_PDEV = 1, > > + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ > > }; > Can drop this as noted above. > > > > > /** > > diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c > > index 8011b8b..4c2f1d3 100644 > > --- a/lib/librte_ether/rte_ethdev.c > > +++ b/lib/librte_ether/rte_ethdev.c > > @@ -64,6 +64,7 @@ > > #include <rte_mbuf.h> > > #include <rte_errno.h> > > #include <rte_spinlock.h> > > +#include <rte_string_fns.h> > > > > #include "rte_ether.h" > > #include "rte_ethdev.h" > > @@ -152,8 +153,21 @@ rte_eth_dev_data_alloc(void) > > RTE_MAX_ETHPORTS * > sizeof(*rte_eth_dev_data)); > > } > > > > +static int > > +rte_eth_dev_name_unique(const char* name) > > +{ > > + unsigned i; > > + > > + for (i = 0; i < nb_ports; i++) { > > + if (strcmp(rte_eth_devices[i].data->name, name) == 0) > > + return -1; > > + } > > + > > + return 0; > > +} > > + > > struct rte_eth_dev * > > -rte_eth_dev_allocate(void) > > +rte_eth_dev_allocate(const char* name) > > { > > struct rte_eth_dev *eth_dev; > > > > @@ -165,23 +179,37 @@ rte_eth_dev_allocate(void) > > if (rte_eth_dev_data == NULL) > > rte_eth_dev_data_alloc(); > > > > + if (rte_eth_dev_name_unique(name)) { > > + PMD_DEBUG_TRACE("Ethernet Device with name %s already > allocated!\n"); > > + return NULL; > > + } > > + > This seems fairly racy if you allow dynamic device creation at run time from the > application, if multiple threads attempt to create bonds in parallel. True but if this is an issue then there probably should be some locking around this allocation anyway. > > > > eth_dev = &rte_eth_devices[nb_ports]; > > eth_dev->data = &rte_eth_dev_data[nb_ports]; > > + rte_snprintf(eth_dev->data->name , sizeof(eth_dev->data->name ), > > + "%s", name); > > eth_dev->data->port_id = nb_ports++; > > return eth_dev; > > } > > > > static int > > rte_eth_dev_init(struct rte_pci_driver *pci_drv, > > - struct rte_pci_device *pci_dev) > > + struct rte_pci_device *pci_dev) > > { > > struct eth_driver *eth_drv; > > struct rte_eth_dev *eth_dev; > > + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; > > + > > int diag; > > > > eth_drv = (struct eth_driver *)pci_drv; > > > > - eth_dev = rte_eth_dev_allocate(); > > + /* Create unique ethdev name by concatenating drive name and number > of > > + * ports */ > > + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", > > + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev- > >addr.function); > > + > > + eth_dev = rte_eth_dev_allocate(ethdev_name); > > if (eth_dev == NULL) > > return -ENOMEM; > > > > diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h > > index 67eda50..27ed0ab 100644 > > --- a/lib/librte_ether/rte_ethdev.h > > +++ b/lib/librte_ether/rte_ethdev.h > > @@ -1233,6 +1233,8 @@ struct rte_eth_dev_sriov { > > }; > > #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) > > > > +#define RTE_ETH_NAME_MAX_LEN (32) > > + > > /** > > * @internal > > * The data part, with no function pointers, associated with each ethernet > device. > > @@ -1241,6 +1243,8 @@ struct rte_eth_dev_sriov { > > * processes in a multi-process configuration. > > */ > > struct rte_eth_dev_data { > > + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ > > + > > void **rx_queues; /**< Array of pointers to RX queues. */ > > void **tx_queues; /**< Array of pointers to TX queues. */ > > uint16_t nb_rx_queues; /**< Number of RX queues. */ > > @@ -1293,10 +1297,11 @@ extern uint8_t rte_eth_dev_count(void); > > * Allocates a new ethdev slot for an ethernet device and returns the pointer > > * to that slot for the driver to use. > > * > > + * @param name Unique identifier name for each Ethernet device > > * @return > > * - Slot in the rte_dev_devices array for a new device; > > */ > > -struct rte_eth_dev *rte_eth_dev_allocate(void); > > +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); > > > > struct eth_driver; > > > > /** > > diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c > b/lib/librte_pmd_pcap/rte_eth_pcap.c > Hmm, we're modifying other pmds for the naming feature, I think it would be best > split out into a separate patch. Something entitled "support unique interface > naming for virtual pmds" or something. True, but if I do that, I will need to break support for virtual devices in this patch set, and I would prefer to keep this functionality working initial release of link bonding. Also another patchset will be required at some point in the near future to tidy up/rationalize the current handling of virtual devices identification and these changes do not change functionally how these devices work. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-13 18:34 ` Doherty, Declan @ 2014-06-13 19:38 ` Neil Horman 2014-06-16 8:59 ` Doherty, Declan 0 siblings, 1 reply; 127+ messages in thread From: Neil Horman @ 2014-06-13 19:38 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev On Fri, Jun 13, 2014 at 06:34:04PM +0000, Doherty, Declan wrote: > > -----Original Message----- > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > Sent: Friday, June 13, 2014 5:08 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > > (librte_eal/librte_ether link bonding support changes) > > > > On Fri, Jun 13, 2014 at 03:41:59PM +0100, Declan Doherty wrote: > > > Updating functionality in EAL to support adding link bonding > > > devices via –vdev option. Link bonding devices will be > > > initialized after all physical devices have been probed and > > > initialized. > > > > > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > > > --- > > > lib/librte_eal/common/eal_common_dev.c | 66 > > +++++++++++++++++++++++++++-- > > > lib/librte_eal/common/eal_common_pci.c | 6 +++ > > > lib/librte_eal/common/include/eal_private.h | 7 +++ > > > lib/librte_eal/common/include/rte_dev.h | 1 + > > > lib/librte_ether/rte_ethdev.c | 34 +++++++++++++-- > > > lib/librte_ether/rte_ethdev.h | 7 ++- > > > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +++++----- > > > lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++------- > > > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > > > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > > > 10 files changed, 144 insertions(+), 36 deletions(-) > > > > > > diff --git a/lib/librte_eal/common/eal_common_dev.c > > b/lib/librte_eal/common/eal_common_dev.c > > > index eae5656..b50c908 100644 > > > --- a/lib/librte_eal/common/eal_common_dev.c > > > +++ b/lib/librte_eal/common/eal_common_dev.c > > > @@ -75,14 +75,28 @@ rte_eal_dev_init(void) > > > > > > /* call the init function for each virtual device */ > > > TAILQ_FOREACH(devargs, &devargs_list, next) { > > > + uint8_t bdev = 0; > > > > > > if (devargs->type != RTE_DEVTYPE_VIRTUAL) > > > continue; > > > > > > TAILQ_FOREACH(driver, &dev_driver_list, next) { > > > - if (driver->type != PMD_VDEV) > > > + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded > > device*/ > > > + if (driver->type != PMD_VDEV && driver->type != > > PMD_BDEV) > > > continue; > > > > > > + /* > > > + * Bonded devices are not initialize here, we do it later in > > > + * rte_eal_bonded_dev_init() after all physical devices > > have been > > > + * probed and initialized > > > + */ > > > + if (driver->type == PMD_BDEV && > > > + !strncmp(driver->name, devargs- > > >virtual.drv_name, > > > + strlen(driver->name))) { > > > + bdev = 1; > > > + break; > > > + } > > > + > > I really don't think you need to add a new device type for bonded devs. Its got > > no specific hardware that it drives, and you configure it with a --vdev command, > > so treat it as one here. I understand that you need to pass additional > > information about slaves to a bonded device, which is fine, but you can do that > > with kvargs pretty easily, at which point its just another vdev. The only other > > requirement is that you initilize the bonded vdev after the slave vdevs have > > been created, which you can do by any of several methods (a priority field to > > indicate that bonded drivers should be initilized last/later, a deferral return > > code from the init routine, or by dead reckoning via the careful construction of > > the application command line (placed the bonded --vdev option last on the > > command line argument list at run time). > > > It was my initial intent to do as you have describe above, but the physical devices > cause a real issue here, physical devices don't call through to rte_eth_dev_allocate until > during rte_eal_pci_probe call, so it's not possible to initialize the bonded device from > within rte_eal_dev_init as the physical devices have not been fully initialized at this > point, as a port_id has not been allocated and can't be added as bonding slaves. I don't > see away around this without changing the EAL API, which I've tried to avoid with this solution. > > Ordering isn't an issue, and can easily be solved if the above problem didn't exist, and although > a new device type isn't technically required, I think it's a cleaner solution than doing > string comparisons. > Ugh, you're right. That stinks. We still shouldn't just drop the init code in with the pci probe code though, especially if dpdk starts supporting more stacked network devices. A notification system for interface creation that you could monitor for pci_device addresses (B:D:F tupples) would be nice, but might be overkill since dpdk doesn't really support device freeing yet. what might be useful is a generic ordering mechanism for rte_eal_dev_init. If you added a priority field to the rte_driver structure, you could create an enumeration for the field to define when the init routine for that driver was called something like: typdef enum { PMD_INIT_FIRST = 0, PMD_INIT_POST_PROBE = 1, } Then you could make multiple calls to rte_eal_dev_init from rte_eal_init at the appropriate places in the initalization process, each time passing one of the above values. rte_eal_dev_init would only initalize those drivers whos priority matched the passed in priority. That would be helpful for interfaces like vlans, vxlans, tunnels, etc. > > > > > > +static int > > > +rte_eth_dev_name_unique(const char* name) > > > +{ > > > + unsigned i; > > > + > > > + for (i = 0; i < nb_ports; i++) { > > > + if (strcmp(rte_eth_devices[i].data->name, name) == 0) > > > + return -1; > > > + } > > > + > > > + return 0; > > > +} > > > + > > > struct rte_eth_dev * > > > -rte_eth_dev_allocate(void) > > > +rte_eth_dev_allocate(const char* name) > > > { > > > struct rte_eth_dev *eth_dev; > > > > > > @@ -165,23 +179,37 @@ rte_eth_dev_allocate(void) > > > if (rte_eth_dev_data == NULL) > > > rte_eth_dev_data_alloc(); > > > > > > + if (rte_eth_dev_name_unique(name)) { > > > + PMD_DEBUG_TRACE("Ethernet Device with name %s already > > allocated!\n"); > > > + return NULL; > > > + } > > > + > > This seems fairly racy if you allow dynamic device creation at run time from the > > application, if multiple threads attempt to create bonds in parallel. > > True but if this is an issue then there probably should be some locking around this allocation anyway. Agreed the allocation of the port id is also racy. > > > > > /** > > > diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c > > b/lib/librte_pmd_pcap/rte_eth_pcap.c > > Hmm, we're modifying other pmds for the naming feature, I think it would be best > > split out into a separate patch. Something entitled "support unique interface > > naming for virtual pmds" or something. > > True, but if I do that, I will need to break support for virtual devices in this patch set, and > I would prefer to keep this functionality working initial release of link bonding. Also another > patchset will be required at some point in the near future to tidy up/rationalize the current > handling of virtual devices identification and these changes do not change functionally how > these devices work. I'm not sure I'm following what you're saying here. If you split this into another patch set, and order earlier in this series, then you won't break anything. I'm not suggesting a functional change here, just a organizational one, so that this change doesn't do more than the changelog indicates. Neil ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-13 19:38 ` Neil Horman @ 2014-06-16 8:59 ` Doherty, Declan 2014-06-16 11:07 ` Neil Horman 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-16 8:59 UTC (permalink / raw) To: Neil Horman; +Cc: dev > -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Friday, June 13, 2014 8:38 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > (librte_eal/librte_ether link bonding support changes) > > On Fri, Jun 13, 2014 at 06:34:04PM +0000, Doherty, Declan wrote: > > > -----Original Message----- > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > Sent: Friday, June 13, 2014 5:08 PM > > > To: Doherty, Declan > > > Cc: dev@dpdk.org > > > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > > > (librte_eal/librte_ether link bonding support changes) > > > > > > On Fri, Jun 13, 2014 at 03:41:59PM +0100, Declan Doherty wrote: > > > > Updating functionality in EAL to support adding link bonding > > > > devices via –vdev option. Link bonding devices will be > > > > initialized after all physical devices have been probed and > > > > initialized. > > > > > > > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > > > > --- > > > > lib/librte_eal/common/eal_common_dev.c | 66 > > > +++++++++++++++++++++++++++-- > > > > lib/librte_eal/common/eal_common_pci.c | 6 +++ > > > > lib/librte_eal/common/include/eal_private.h | 7 +++ > > > > lib/librte_eal/common/include/rte_dev.h | 1 + > > > > lib/librte_ether/rte_ethdev.c | 34 +++++++++++++-- > > > > lib/librte_ether/rte_ethdev.h | 7 ++- > > > > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +++++----- > > > > lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++------- > > > > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > > > > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > > > > 10 files changed, 144 insertions(+), 36 deletions(-) > > > > > > > > diff --git a/lib/librte_eal/common/eal_common_dev.c > > > b/lib/librte_eal/common/eal_common_dev.c > > > > index eae5656..b50c908 100644 > > > > --- a/lib/librte_eal/common/eal_common_dev.c > > > > +++ b/lib/librte_eal/common/eal_common_dev.c > > > > @@ -75,14 +75,28 @@ rte_eal_dev_init(void) > > > > > > > > /* call the init function for each virtual device */ > > > > TAILQ_FOREACH(devargs, &devargs_list, next) { > > > > + uint8_t bdev = 0; > > > > > > > > if (devargs->type != RTE_DEVTYPE_VIRTUAL) > > > > continue; > > > > > > > > TAILQ_FOREACH(driver, &dev_driver_list, next) { > > > > - if (driver->type != PMD_VDEV) > > > > + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded > > > device*/ > > > > + if (driver->type != PMD_VDEV && driver->type != > > > PMD_BDEV) > > > > continue; > > > > > > > > + /* > > > > + * Bonded devices are not initialize here, we do it later in > > > > + * rte_eal_bonded_dev_init() after all physical devices > > > have been > > > > + * probed and initialized > > > > + */ > > > > + if (driver->type == PMD_BDEV && > > > > + !strncmp(driver->name, devargs- > > > >virtual.drv_name, > > > > + strlen(driver->name))) { > > > > + bdev = 1; > > > > + break; > > > > + } > > > > + > > > I really don't think you need to add a new device type for bonded devs. Its got > > > no specific hardware that it drives, and you configure it with a --vdev > command, > > > so treat it as one here. I understand that you need to pass additional > > > information about slaves to a bonded device, which is fine, but you can do that > > > with kvargs pretty easily, at which point its just another vdev. The only other > > > requirement is that you initilize the bonded vdev after the slave vdevs have > > > been created, which you can do by any of several methods (a priority field to > > > indicate that bonded drivers should be initilized last/later, a deferral return > > > code from the init routine, or by dead reckoning via the careful construction > of > > > the application command line (placed the bonded --vdev option last on the > > > command line argument list at run time). > > > > > It was my initial intent to do as you have describe above, but the physical devices > > cause a real issue here, physical devices don't call through to > rte_eth_dev_allocate until > > during rte_eal_pci_probe call, so it's not possible to initialize the bonded device > from > > within rte_eal_dev_init as the physical devices have not been fully initialized at > this > > point, as a port_id has not been allocated and can't be added as bonding slaves. I > don't > > see away around this without changing the EAL API, which I've tried to avoid with > this solution. > > > > Ordering isn't an issue, and can easily be solved if the above problem didn't > exist, and although > > a new device type isn't technically required, I think it's a cleaner solution than > doing > > string comparisons. > > > > > Ugh, you're right. That stinks. We still shouldn't just drop the init code in > with the pci probe code though, especially if dpdk starts supporting more > stacked network devices. A notification system for interface creation that you > could monitor for pci_device addresses (B:D:F tupples) would be nice, but might > be overkill since dpdk doesn't really support device freeing yet. what might be > useful is a generic ordering mechanism for rte_eal_dev_init. If you added a > priority field to the rte_driver structure, you could create an enumeration for > the field to define when the init routine for that driver was called something > like: > typdef enum { > PMD_INIT_FIRST = 0, > PMD_INIT_POST_PROBE = 1, > } > > Then you could make multiple calls to rte_eal_dev_init from rte_eal_init at the > appropriate places in the initalization process, each time passing one of the > above values. rte_eal_dev_init would only initalize those drivers whos priority > matched the passed in priority. That would be helpful for interfaces like > vlans, vxlans, tunnels, etc. > I'm not sure I understand how you intend this to work, are you proposing that rte_eal_dev_init is call multiple times by the user pre and post rte_eal_pci_probe this doesn't seem like an idea solution either. I'm not 100% clear why rte_eal_pci_probe is currently called by the application code and not initiated from within rte_eal_dev_init, if this was the case we would be able to figure out a dependency tree for initialization of devices, based on the parsed input arguments without having extra user input to or multiple calls of rte_eal_dev_init. Either way I don't think that any of these solution are trivial and they would need to be thoroughly tested, which I don't think will be possible in the time frame for 1.7. If my current implementation calling rte_eal_bonded_dev_init from within rte_eal_pci_probe is unacceptable, then I propose that I remove the EAL changes for --vdev initialization of bonding devices from this release and only support the C API, and that an overall strategy is discussed for static device initialization that is developed for 1.7.1 or 1.8. > > > > > > > > +static int > > > > +rte_eth_dev_name_unique(const char* name) > > > > +{ > > > > + unsigned i; > > > > + > > > > + for (i = 0; i < nb_ports; i++) { > > > > + if (strcmp(rte_eth_devices[i].data->name, name) == 0) > > > > + return -1; > > > > + } > > > > + > > > > + return 0; > > > > +} > > > > + > > > > struct rte_eth_dev * > > > > -rte_eth_dev_allocate(void) > > > > +rte_eth_dev_allocate(const char* name) > > > > { > > > > struct rte_eth_dev *eth_dev; > > > > > > > > @@ -165,23 +179,37 @@ rte_eth_dev_allocate(void) > > > > if (rte_eth_dev_data == NULL) > > > > rte_eth_dev_data_alloc(); > > > > > > > > + if (rte_eth_dev_name_unique(name)) { > > > > + PMD_DEBUG_TRACE("Ethernet Device with name %s already > > > allocated!\n"); > > > > + return NULL; > > > > + } > > > > + > > > This seems fairly racy if you allow dynamic device creation at run time from > the > > > application, if multiple threads attempt to create bonds in parallel. > > > > True but if this is an issue then there probably should be some locking around > this allocation anyway. > Agreed the allocation of the port id is also racy. > > > > > > > > /** > > > > diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c > > > b/lib/librte_pmd_pcap/rte_eth_pcap.c > > > Hmm, we're modifying other pmds for the naming feature, I think it would be > best > > > split out into a separate patch. Something entitled "support unique interface > > > naming for virtual pmds" or something. > > > > True, but if I do that, I will need to break support for virtual devices in this patch > set, and > > I would prefer to keep this functionality working initial release of link bonding. > Also another > > patchset will be required at some point in the near future to tidy up/rationalize > the current > > handling of virtual devices identification and these changes do not change > functionally how > > these devices work. > > I'm not sure I'm following what you're saying here. If you split this into > another patch set, and order earlier in this series, then you won't break > anything. I'm not suggesting a functional change here, just a organizational > one, so that this change doesn't do more than the changelog indicates. > Sorry I misunderstood, I thought that you where proposing that I create a completely new patchset separate to the link bonding release set for these changes. > Neil Declan ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-16 8:59 ` Doherty, Declan @ 2014-06-16 11:07 ` Neil Horman 2014-06-16 16:17 ` Richardson, Bruce 0 siblings, 1 reply; 127+ messages in thread From: Neil Horman @ 2014-06-16 11:07 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev On Mon, Jun 16, 2014 at 08:59:25AM +0000, Doherty, Declan wrote: > > -----Original Message----- > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > Sent: Friday, June 13, 2014 8:38 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > > (librte_eal/librte_ether link bonding support changes) > > > > On Fri, Jun 13, 2014 at 06:34:04PM +0000, Doherty, Declan wrote: > > > > -----Original Message----- > > > > From: Neil Horman [mailto:nhorman@tuxdriver.com] > > > > Sent: Friday, June 13, 2014 5:08 PM > > > > To: Doherty, Declan > > > > Cc: dev@dpdk.org > > > > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > > > > (librte_eal/librte_ether link bonding support changes) > > > > > > > > On Fri, Jun 13, 2014 at 03:41:59PM +0100, Declan Doherty wrote: > > > > > Updating functionality in EAL to support adding link bonding > > > > > devices via –vdev option. Link bonding devices will be > > > > > initialized after all physical devices have been probed and > > > > > initialized. > > > > > > > > > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > > > > > --- > > > > > lib/librte_eal/common/eal_common_dev.c | 66 > > > > +++++++++++++++++++++++++++-- > > > > > lib/librte_eal/common/eal_common_pci.c | 6 +++ > > > > > lib/librte_eal/common/include/eal_private.h | 7 +++ > > > > > lib/librte_eal/common/include/rte_dev.h | 1 + > > > > > lib/librte_ether/rte_ethdev.c | 34 +++++++++++++-- > > > > > lib/librte_ether/rte_ethdev.h | 7 ++- > > > > > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +++++----- > > > > > lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++------- > > > > > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > > > > > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > > > > > 10 files changed, 144 insertions(+), 36 deletions(-) > > > > > > > > > > diff --git a/lib/librte_eal/common/eal_common_dev.c > > > > b/lib/librte_eal/common/eal_common_dev.c > > > > > index eae5656..b50c908 100644 > > > > > --- a/lib/librte_eal/common/eal_common_dev.c > > > > > +++ b/lib/librte_eal/common/eal_common_dev.c > > > > > @@ -75,14 +75,28 @@ rte_eal_dev_init(void) > > > > > > > > > > /* call the init function for each virtual device */ > > > > > TAILQ_FOREACH(devargs, &devargs_list, next) { > > > > > + uint8_t bdev = 0; > > > > > > > > > > if (devargs->type != RTE_DEVTYPE_VIRTUAL) > > > > > continue; > > > > > > > > > > TAILQ_FOREACH(driver, &dev_driver_list, next) { > > > > > - if (driver->type != PMD_VDEV) > > > > > + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded > > > > device*/ > > > > > + if (driver->type != PMD_VDEV && driver->type != > > > > PMD_BDEV) > > > > > continue; > > > > > > > > > > + /* > > > > > + * Bonded devices are not initialize here, we do it later in > > > > > + * rte_eal_bonded_dev_init() after all physical devices > > > > have been > > > > > + * probed and initialized > > > > > + */ > > > > > + if (driver->type == PMD_BDEV && > > > > > + !strncmp(driver->name, devargs- > > > > >virtual.drv_name, > > > > > + strlen(driver->name))) { > > > > > + bdev = 1; > > > > > + break; > > > > > + } > > > > > + > > > > I really don't think you need to add a new device type for bonded devs. Its got > > > > no specific hardware that it drives, and you configure it with a --vdev > > command, > > > > so treat it as one here. I understand that you need to pass additional > > > > information about slaves to a bonded device, which is fine, but you can do that > > > > with kvargs pretty easily, at which point its just another vdev. The only other > > > > requirement is that you initilize the bonded vdev after the slave vdevs have > > > > been created, which you can do by any of several methods (a priority field to > > > > indicate that bonded drivers should be initilized last/later, a deferral return > > > > code from the init routine, or by dead reckoning via the careful construction > > of > > > > the application command line (placed the bonded --vdev option last on the > > > > command line argument list at run time). > > > > > > > It was my initial intent to do as you have describe above, but the physical devices > > > cause a real issue here, physical devices don't call through to > > rte_eth_dev_allocate until > > > during rte_eal_pci_probe call, so it's not possible to initialize the bonded device > > from > > > within rte_eal_dev_init as the physical devices have not been fully initialized at > > this > > > point, as a port_id has not been allocated and can't be added as bonding slaves. I > > don't > > > see away around this without changing the EAL API, which I've tried to avoid with > > this solution. > > > > > > Ordering isn't an issue, and can easily be solved if the above problem didn't > > exist, and although > > > a new device type isn't technically required, I think it's a cleaner solution than > > doing > > > string comparisons. > > > > > > > > > Ugh, you're right. That stinks. We still shouldn't just drop the init code in > > with the pci probe code though, especially if dpdk starts supporting more > > stacked network devices. A notification system for interface creation that you > > could monitor for pci_device addresses (B:D:F tupples) would be nice, but might > > be overkill since dpdk doesn't really support device freeing yet. what might be > > useful is a generic ordering mechanism for rte_eal_dev_init. If you added a > > priority field to the rte_driver structure, you could create an enumeration for > > the field to define when the init routine for that driver was called something > > like: > > typdef enum { > > PMD_INIT_FIRST = 0, > > PMD_INIT_POST_PROBE = 1, > > } > > > > Then you could make multiple calls to rte_eal_dev_init from rte_eal_init at the > > appropriate places in the initalization process, each time passing one of the > > above values. rte_eal_dev_init would only initalize those drivers whos priority > > matched the passed in priority. That would be helpful for interfaces like > > vlans, vxlans, tunnels, etc. > > > I'm not sure I understand how you intend this to work, are you proposing that > rte_eal_dev_init is call multiple times by the user pre and post rte_eal_pci_probe Mostly, I'm proposing that rte_eal_pci_probe be called by rte_eal_init, and that we then call rte_eal_dev_init multiple times from within rte_eal_dev_init, passing a parameter to indicate the priority level of device that should be initalized within that call. > this doesn't seem like an idea solution either. I'm not 100% clear why > rte_eal_pci_probe is currently called by the application code and not initiated > from within rte_eal_dev_init, if this was the case we would be able to figure out > a dependency tree for initialization of devices, based on the parsed input > arguments without having extra user input to or multiple calls of rte_eal_dev_init. > I agree, I think you should move it into its proper place within rte_eal_init, though I think multiple calls to rte_eal_dev_init is perfectly acceptible in this scenario. You certainly could parse the command line to build a dependency tree if you wish, though I don't think thats crruently overly complex. The current dependency tree is static at (physical devices, virtual devices, probe, stacked devices). If you need something more complex later, you can re-write it freely as it will be an internal library mechanism with no external visibility. > Either way I don't think that any of these solution are trivial and they would > need to be thoroughly tested, which I don't think will be possible in the time > frame for 1.7. If my current implementation calling rte_eal_bonded_dev_init > from within rte_eal_pci_probe is unacceptable, then I propose that I remove > the EAL changes for --vdev initialization of bonding devices from this release and > only support the C API, and that an overall strategy is discussed for static device > initialization that is developed for 1.7.1 or 1.8. > I would assert that if you can't write the pmd to support the existing pmd interfaces, then this probably needs to wait until 1.8. I'll bow to consensus on that, but I would think that, if it gets in now, the additional work won't happen. Neil > > > > > > > > > > +static int > > > > > +rte_eth_dev_name_unique(const char* name) > > > > > +{ > > > > > + unsigned i; > > > > > + > > > > > + for (i = 0; i < nb_ports; i++) { > > > > > + if (strcmp(rte_eth_devices[i].data->name, name) == 0) > > > > > + return -1; > > > > > + } > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > struct rte_eth_dev * > > > > > -rte_eth_dev_allocate(void) > > > > > +rte_eth_dev_allocate(const char* name) > > > > > { > > > > > struct rte_eth_dev *eth_dev; > > > > > > > > > > @@ -165,23 +179,37 @@ rte_eth_dev_allocate(void) > > > > > if (rte_eth_dev_data == NULL) > > > > > rte_eth_dev_data_alloc(); > > > > > > > > > > + if (rte_eth_dev_name_unique(name)) { > > > > > + PMD_DEBUG_TRACE("Ethernet Device with name %s already > > > > allocated!\n"); > > > > > + return NULL; > > > > > + } > > > > > + > > > > This seems fairly racy if you allow dynamic device creation at run time from > > the > > > > application, if multiple threads attempt to create bonds in parallel. > > > > > > True but if this is an issue then there probably should be some locking around > > this allocation anyway. > > Agreed the allocation of the port id is also racy. > > > > > > > > > > > /** > > > > > diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c > > > > b/lib/librte_pmd_pcap/rte_eth_pcap.c > > > > Hmm, we're modifying other pmds for the naming feature, I think it would be > > best > > > > split out into a separate patch. Something entitled "support unique interface > > > > naming for virtual pmds" or something. > > > > > > True, but if I do that, I will need to break support for virtual devices in this patch > > set, and > > > I would prefer to keep this functionality working initial release of link bonding. > > Also another > > > patchset will be required at some point in the near future to tidy up/rationalize > > the current > > > handling of virtual devices identification and these changes do not change > > functionally how > > > these devices work. > > > > I'm not sure I'm following what you're saying here. If you split this into > > another patch set, and order earlier in this series, then you won't break > > anything. I'm not suggesting a functional change here, just a organizational > > one, so that this change doesn't do more than the changelog indicates. > > > > Sorry I misunderstood, I thought that you where proposing that I create a completely new > patchset separate to the link bonding release set for these changes. > > > Neil > > Declan ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-16 11:07 ` Neil Horman @ 2014-06-16 16:17 ` Richardson, Bruce 2014-06-16 17:47 ` Neil Horman 0 siblings, 1 reply; 127+ messages in thread From: Richardson, Bruce @ 2014-06-16 16:17 UTC (permalink / raw) To: Neil Horman, Doherty, Declan; +Cc: dev > -----Original Message----- <...snip...> > > this doesn't seem like an idea solution either. I'm not 100% clear why > > rte_eal_pci_probe is currently called by the application code and not initiated > > from within rte_eal_dev_init, if this was the case we would be able to figure > out > > a dependency tree for initialization of devices, based on the parsed input > > arguments without having extra user input to or multiple calls of > rte_eal_dev_init. > > > I agree, I think you should move it into its proper place within rte_eal_init, > though I think multiple calls to rte_eal_dev_init is perfectly acceptible in > this scenario. You certainly could parse the command line to build a dependency > tree if you wish, though I don't think thats crruently overly complex. The current > dependency tree is static at (physical devices, virtual devices, probe, stacked > devices). If you need something more complex later, you can re-write it freely > as it will be an internal library mechanism with no external visibility. > How useful is this going to be? The more complicated the ethdev stacking being done, the better suited it is to being done via the C APIs, via code which can do analysis of the hardware and cores at runtime and make logical decisions? After all, even if we do have applications being run to use bonded devices at runtime in place of physical ones, the bonded devices don't actually replace the physical ones, so some other application logic is needed to ensure that the application knows to only use the bonded devices. In most cases simply having a port-mask suffices, but not all apps will have a port-mask parameter, and for those that do, it introduces complexity for the user counting out interfaces and trying to work out what ports will have what numbers to set the appropriate bits in the portmask. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-16 16:17 ` Richardson, Bruce @ 2014-06-16 17:47 ` Neil Horman 2014-06-16 18:07 ` Richardson, Bruce 2014-06-16 18:09 ` Thomas Monjalon 0 siblings, 2 replies; 127+ messages in thread From: Neil Horman @ 2014-06-16 17:47 UTC (permalink / raw) To: Richardson, Bruce; +Cc: dev On Mon, Jun 16, 2014 at 04:17:03PM +0000, Richardson, Bruce wrote: > > > > -----Original Message----- > <...snip...> > > > this doesn't seem like an idea solution either. I'm not 100% clear why > > > rte_eal_pci_probe is currently called by the application code and not initiated > > > from within rte_eal_dev_init, if this was the case we would be able to figure > > out > > > a dependency tree for initialization of devices, based on the parsed input > > > arguments without having extra user input to or multiple calls of > > rte_eal_dev_init. > > > > > I agree, I think you should move it into its proper place within rte_eal_init, > > though I think multiple calls to rte_eal_dev_init is perfectly acceptible in > > this scenario. You certainly could parse the command line to build a dependency > > tree if you wish, though I don't think thats crruently overly complex. The current > > dependency tree is static at (physical devices, virtual devices, probe, stacked > > devices). If you need something more complex later, you can re-write it freely > > as it will be an internal library mechanism with no external visibility. > > > > How useful is this going to be? The more complicated the ethdev stacking being done, the better suited it is to being done via the C APIs, via code which can do analysis of the hardware and cores at runtime and make logical decisions? After all, even if we do have applications being run to use bonded devices at runtime in place of physical ones, the bonded devices don't actually replace the physical ones, so some other application logic is needed to ensure that the application knows to only use the bonded devices. In most cases simply having a port-mask suffices, but not all apps will have a port-mask parameter, and for those that do, it introduces complexity for the user counting out interfaces and trying to work out what ports will have what numbers to set the appropriate bits in the portmask. Honestly? I don't know. As Stephen indicates the command line options might not be overly used, as its not always the best interface to select when building an application. But by the same token, not everyone is building an application that needs dynamic configuration with DPDK. My main concern here is one of consistency, which is really what people look for in a package within a distribution (i.e. Fedora, what I'm doing with the dpdk right now). You're probably correct in that lots of people will use the C api to build bonded interfaces since its a new api and they won't have been using bonds yet. But I'm concerned that, with a distributed package, lots of people might also be porting legacy applications to use the DPDK, in which case they may very well want to create static configurations within the dpdk to lower their porting efforts. Those people may well be very turned off by the fact that some, but not all interfaces can use the command line to be configured. In the end, its all about consistency in my mind. I get that the --vdev command line parameter perhaps isn't the most useful interface available, but its whats there. And if you start creating PMD's that use separate configuration interfaces and abandon the ones before it, you'll wind up with a hodgepodge of apis, all of which an application will have to be aware of to provide a full robust feature set. If --vdev stays, we should make it work for all the PMD's. Most might not use it, but some will, and those that do will appreciate the consistency you provide. If it just doesn't make sense to keep it around anymore, lets drop it and replace it with something better. Perhaps a configuration file interface would be good, so that the initialization of the pmds and the creation of interfaces is separated from the acutal application (thats actually a good idea I think, as it implies that all an application needs to know about is interfaces, not how they are constructed or stacked). But lets not just quietly start abandoning stuff because its inconvienient. Neil ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-16 17:47 ` Neil Horman @ 2014-06-16 18:07 ` Richardson, Bruce 2014-06-16 18:09 ` Thomas Monjalon 1 sibling, 0 replies; 127+ messages in thread From: Richardson, Bruce @ 2014-06-16 18:07 UTC (permalink / raw) To: Neil Horman; +Cc: dev > -----Original Message----- > From: Neil Horman [mailto:nhorman@tuxdriver.com] > Sent: Monday, June 16, 2014 10:48 AM > To: Richardson, Bruce > Cc: Doherty, Declan; dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > (librte_eal/librte_ether link bonding support changes) > > On Mon, Jun 16, 2014 at 04:17:03PM +0000, Richardson, Bruce wrote: > > > > > > > -----Original Message----- > > <...snip...> > > > > this doesn't seem like an idea solution either. I'm not 100% clear why > > > > rte_eal_pci_probe is currently called by the application code and not > initiated > > > > from within rte_eal_dev_init, if this was the case we would be able to > figure > > > out > > > > a dependency tree for initialization of devices, based on the parsed input > > > > arguments without having extra user input to or multiple calls of > > > rte_eal_dev_init. > > > > > > > I agree, I think you should move it into its proper place within rte_eal_init, > > > though I think multiple calls to rte_eal_dev_init is perfectly acceptible in > > > this scenario. You certainly could parse the command line to build a > dependency > > > tree if you wish, though I don't think thats crruently overly complex. The > current > > > dependency tree is static at (physical devices, virtual devices, probe, stacked > > > devices). If you need something more complex later, you can re-write it > freely > > > as it will be an internal library mechanism with no external visibility. > > > > > > > How useful is this going to be? The more complicated the ethdev stacking > being done, the better suited it is to being done via the C APIs, via code which > can do analysis of the hardware and cores at runtime and make logical > decisions? After all, even if we do have applications being run to use bonded > devices at runtime in place of physical ones, the bonded devices don't actually > replace the physical ones, so some other application logic is needed to ensure > that the application knows to only use the bonded devices. In most cases simply > having a port-mask suffices, but not all apps will have a port-mask parameter, > and for those that do, it introduces complexity for the user counting out > interfaces and trying to work out what ports will have what numbers to set the > appropriate bits in the portmask. > > Honestly? I don't know. As Stephen indicates the command line options might > not be overly used, as its not always the best interface to select when building > an application. But by the same token, not everyone is building an application > that needs dynamic configuration with DPDK. My main concern here is one of > consistency, which is really what people look for in a package within a > distribution (i.e. Fedora, what I'm doing with the dpdk right now). You're > probably correct in that lots of people will use the C api to build bonded > interfaces since its a new api and they won't have been using bonds yet. But > I'm concerned that, with a distributed package, lots of people might also be > porting legacy applications to use the DPDK, in which case they may very well > want to create static configurations within the dpdk to lower their porting > efforts. Those people may well be very turned off by the fact that some, but > not all interfaces can use the command line to be configured. > > In the end, its all about consistency in my mind. I get that the --vdev command > line parameter perhaps isn't the most useful interface available, but its whats > there. And if you start creating PMD's that use separate configuration > interfaces and abandon the ones before it, you'll wind up with a hodgepodge of > apis, all of which an application will have to be aware of to provide a full > robust feature set. > > If --vdev stays, we should make it work for all the PMD's. Most might not use > it, but some will, and those that do will appreciate the consistency you > provide. If it just doesn't make sense to keep it around anymore, lets drop it > and replace it with something better. Perhaps a configuration file interface > would be good, so that the initialization of the pmds and the creation of > interfaces is separated from the acutal application (thats actually a good idea > I think, as it implies that all an application needs to know about is > interfaces, not how they are constructed or stacked). But lets not just quietly > start abandoning stuff because its inconvienient. > The config file idea is one that's probably worth considering for the future. The thing about the vpmd option the assumption that all PMDs are equal. It works well for creating pseudo-physical ethdevs, such as pcap, raw-socket or ring or other similar ethdevs, but I'm not sure it's a good abstraction for higher level functionality that we want to expose via an ethdev interface. However, what to do for all these is not a decision we need to make for 1.7 :-) ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-16 17:47 ` Neil Horman 2014-06-16 18:07 ` Richardson, Bruce @ 2014-06-16 18:09 ` Thomas Monjalon 1 sibling, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-16 18:09 UTC (permalink / raw) To: dev 2014-06-16 13:47, Neil Horman: > But lets not just quietly start abandoning stuff because its inconvienient. Fully agree. The minimum is to have "TODO" comments. And please, don't consider existing API as a reference. We must build a convenient and robust API, and probably break the existing one in many areas. The command line could be also reworked. Thanks for your efforts -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) Declan Doherty 2014-06-13 16:08 ` Neil Horman @ 2014-06-13 21:59 ` Stephen Hemminger 2014-06-16 7:59 ` Doherty, Declan 1 sibling, 1 reply; 127+ messages in thread From: Stephen Hemminger @ 2014-06-13 21:59 UTC (permalink / raw) To: Declan Doherty; +Cc: dev On Fri, 13 Jun 2014 15:41:59 +0100 Declan Doherty <declan.doherty@intel.com> wrote: > Updating functionality in EAL to support adding link bonding > devices via –vdev option. Link bonding devices will be > initialized after all physical devices have been probed and > initialized. > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> The DPDK is used by both static (demo style) applications and dynamic applications that need to be able to modify parameters while running. The tendency of the code has been to have values that can only be set at initialization, and this kind of inflexibility is hard to fix. The whole dev args style of configuration is a poor design for those reasons. How do you support creating additional bond devices at runtime? Remember what works for a demo doesn't make a good API for real world. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) 2014-06-13 21:59 ` Stephen Hemminger @ 2014-06-16 7:59 ` Doherty, Declan 0 siblings, 0 replies; 127+ messages in thread From: Doherty, Declan @ 2014-06-16 7:59 UTC (permalink / raw) To: Stephen Hemminger; +Cc: dev > -----Original Message----- > From: Stephen Hemminger [mailto:stephen@networkplumber.org] > Sent: Friday, June 13, 2014 10:59 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library > (librte_eal/librte_ether link bonding support changes) > > On Fri, 13 Jun 2014 15:41:59 +0100 > Declan Doherty <declan.doherty@intel.com> wrote: > > > Updating functionality in EAL to support adding link bonding > > devices via –vdev option. Link bonding devices will be > > initialized after all physical devices have been probed and > > initialized. > > > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > > The DPDK is used by both static (demo style) applications and dynamic > applications that need to be able to modify parameters while running. > The tendency of the code has been to have values that can only be set > at initialization, and this kind of inflexibility is hard to fix. > > The whole dev args style of configuration is a poor design for those > reasons. How do you support creating additional bond devices at runtime? > > Remember what works for a demo doesn't make a good API for real world. > > Hi Stephen, I totally agree but all of the dev argument parsing / static initialization here is based on the rte_eth_bond.h public API. Actually testpmd has full support for dynamic initialization and management of bonding devices in run-time through the interactive command line options which pretty much have options corresponding to all of the public link bonding APIs. It is my intent that this would main interface that this API would be used by user applications and that the static configuration options using --vdev interface would only be used if a user wanted to use a bonded device in a existing application without having to make any changes to their code or add any explicit reference to the link bonding library. ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v3 3/5] Link Bonding PMD Library (Unit Test Suite) 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 1/5] " Declan Doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) Declan Doherty @ 2014-06-13 14:42 ` Declan Doherty 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 4/5] Link Bonding PMD Library (testpmd link bonding API support) Declan Doherty ` (9 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-13 14:42 UTC (permalink / raw) To: dev, dev Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 288 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4983 insertions(+), 1 deletion(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 3b050c3..02eff0f 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -96,7 +96,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index 016410d..c31389d 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -157,6 +157,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -225,6 +229,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..38db383 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,288 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} + diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index f992ceb..574841b 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -96,6 +96,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..f76a6d5 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d",i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2",BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v3 4/5] Link Bonding PMD Library (testpmd link bonding API support) 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (2 preceding siblings ...) 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 3/5] Link Bonding PMD Library (Unit Test Suite) Declan Doherty @ 2014-06-13 14:42 ` Declan Doherty 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 5/5] Link Bonding PMD Library (Doxygen Additions) Declan Doherty ` (8 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-13 14:42 UTC (permalink / raw) To: dev, dev Adding link bonding support to testpmd. - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 571 ++++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 37 ++- app/test-pmd/testpmd.h | 2 + 5 files changed, 611 insertions(+), 6 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 4678977..fd220e7 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -400,6 +403,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -2856,6 +2884,539 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + + int port_id; + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + port_id = rte_eth_bond_create("testpmd-bonded-dev", res->mode, res->socket); + /* Create a new bonded device. */ + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("\t Created new bonded device (port %d).\n", port_id); + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -5567,6 +6128,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 52ad01a..d1ec1e5 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -242,6 +242,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -255,7 +256,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index eb14cd3..63669f5 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 2529dc3..e3de4ad 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1246,7 +1272,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1374,10 +1400,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 4fabf1c..892d39f 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v3 5/5] Link Bonding PMD Library (Doxygen Additions) 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (3 preceding siblings ...) 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 4/5] Link Bonding PMD Library (testpmd link bonding API support) Declan Doherty @ 2014-06-13 14:42 ` Declan Doherty 2014-06-13 15:20 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Neil Horman ` (7 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-13 14:42 UTC (permalink / raw) To: dev, dev Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 6e75a6e..a4d2e57 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_eth_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index 9df7356..92f8c59 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_pmd_bond \ lib/librte_eal/common/include \ lib/librte_distributor \ lib/librte_ether \ -- 1.8.5.3 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (4 preceding siblings ...) 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 5/5] Link Bonding PMD Library (Doxygen Additions) Declan Doherty @ 2014-06-13 15:20 ` Neil Horman 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty ` (6 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Neil Horman @ 2014-06-13 15:20 UTC (permalink / raw) To: Declan Doherty; +Cc: dev On Fri, Jun 13, 2014 at 03:41:57PM +0100, Declan Doherty wrote: > This patch contains the initial release of the Link Bonding PMD Library > > Supporting bonding modes: > 0 - Round Robin > 1 - Active Backup > 2 - Balance (Supporting 3 transmission polices) > layer 2, layer 2+3, layer 3+4 > 3 - Broadcast > > > Version 3 of this patch set add the following functionality changes > - Link bonding command line option parsing / initialization support > - Unique name identifier to rte_eth_dev_data struct to identify > virtual ethdev's which are to be added to bondded device from > command line. > - Updates to EAL to support initialization of link bonding devices > > Patch Set Description: > 0001 - librte_pmd_bond + makefile changes > 0002 - librte_eal / librte_ether changes to support bonding device intialization > 0003 - link bonding unti test suite > 0004 - testpmd link bonding support changes > 0005 - doxygen additions > > > app/test-pmd/cmdline.c | 571 ++++ > app/test-pmd/config.c | 4 +- > app/test-pmd/parameters.c | 3 + > app/test-pmd/testpmd.c | 37 +- > app/test-pmd/testpmd.h | 2 + > app/test/Makefile | 4 +- > app/test/commands.c | 7 + > app/test/packet_burst_generator.c | 288 ++ > app/test/packet_burst_generator.h | 78 + > app/test/test.h | 1 + > app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ > app/test/virtual_pmd.c | 574 ++++ > app/test/virtual_pmd.h | 74 + > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_eal/common/eal_common_dev.c | 66 +- > lib/librte_eal/common/eal_common_pci.c | 6 + > lib/librte_eal/common/include/eal_private.h | 7 + > lib/librte_eal/common/include/rte_dev.h | 1 + > lib/librte_ether/rte_ethdev.c | 34 +- > lib/librte_ether/rte_ethdev.h | 7 +- > lib/librte_pmd_bond/Makefile | 32 + > lib/librte_pmd_bond/rte_eth_bond.c | 2149 +++++++++++++++ > lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- > lib/librte_pmd_ring/rte_eth_ring.c | 32 +- > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > mk/rte.app.mk | 5 + > 32 files changed, 8192 insertions(+), 43 deletions(-) > create mode 100644 app/test/packet_burst_generator.c > create mode 100644 app/test/packet_burst_generator.h > create mode 100644 app/test/test_link_bonding.c > create mode 100644 app/test/virtual_pmd.c > create mode 100644 app/test/virtual_pmd.h > create mode 100644 lib/librte_pmd_bond/Makefile > create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c > create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h > > -- > 1.8.5.3 > > This looks alot better, thank you. I've got a few comments in the individual patches, but on the whole, well done! Neil ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v4 0/6] Link Bonding Library 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (5 preceding siblings ...) 2014-06-13 15:20 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Neil Horman @ 2014-06-16 11:18 ` Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty ` (6 more replies) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 1/6] Link Bonding Library (lib/librte_pmd_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, la Mode 3 - Broadcast Declan Doherty ` (5 subsequent siblings) 12 siblings, 7 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-16 11:18 UTC (permalink / raw) To: dev AThis patch contains the initial release of the Link Bonding PMD Library Supporting bonding modes: 0 - Round Robin 1 - Active Backup 2 - Balance (Supporting 3 transmission polices) layer 2, layer 2+3, layer 3+4 3 - Broadcast Version 4 of patch set: - Fixes some checkpatch formatting issues - Splits unique pmd naming and link bonding intialization changes in EAL into separate patches Patch Set Description: 0001 - librte_pmd_bond + makefile changes 0002 - librte_ether changes to support unique naming of pmds 0003 - librte_eal changes to support bonding device intialization 0005 - link bonding unti test suite 0005 - testpmd link bonding support changes 0006 - doxygen additions Declan Doherty (6): Link Bonding Library (lib/librte_pmd_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, la Mode 3 - Broadcast Support for unique interface naming of pmds EAL support for link bonding device initialization Link bonding Unit Tests testpmd link bonding additions Link Bonding Library doxygen additions app/test-pmd/cmdline.c | 571 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 37 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 288 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/common/eal_common_dev.c | 66 +- lib/librte_eal/common/eal_common_pci.c | 6 + lib/librte_eal/common/include/eal_private.h | 7 + lib/librte_eal/common/include/rte_dev.h | 1 + lib/librte_ether/rte_ethdev.c | 33 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2149 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 5 + 32 files changed, 8191 insertions(+), 43 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v5 0/6] Link Bonding Library 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty @ 2014-06-18 16:14 ` Declan Doherty 2014-06-18 16:18 ` Neil Horman ` (7 more replies) 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty ` (5 subsequent siblings) 6 siblings, 8 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-18 16:14 UTC (permalink / raw) To: dev This patch contains the initial release of the Link Bonding PMD Library Supporting bonding modes: 0 - Round Robin 1 - Active Backup 2 - Balance (Supporting 3 transmission polices) layer 2, layer 2+3, layer 3+4 3 - Broadcast Version 5 of patch set: Contains changes to EAL code to allow initialisation of Bonded devices from application startup options. rte_eal_init now calls rte_eal_pci_probe between calling rte_eal_dev_init with PRE and POST PCI probe flags. This gets around polluting the eal pci code with references to link bonding devices. Also rte_eal_pci_probe can now be called multiple times and will not try to re-initialize the driver if one already exists, this means that existing applications which currently call rte_eal_pci_probe will not be affected by this change Patch Set Description: 0001 - librte_pmd_bond + makefile changes 0002 - librte_ether changes to support unique naming of pmds 0003 - librte_eal changes to support bonding device intialization 0005 - link bonding unti test suite 0005 - testpmd link bonding support changes 0006 - doxygen additions Declan Doherty (6): Link Bonding Library (lib/librte_pmd_bond) Support for unique interface naming of pmds EAL support for link bonding device initialization Link bonding Unit Tests testpmd link bonding additions Link Bonding Library doxygen additions app/test-pmd/cmdline.c | 579 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/bsdapp/eal/eal.c | 10 +- lib/librte_eal/common/eal_common_dev.c | 58 +- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 - lib/librte_eal/common/include/rte_dev.h | 13 +- lib/librte_eal/linuxapp/eal/eal.c | 11 +- lib/librte_ether/rte_ethdev.c | 32 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2148 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 5 + 34 files changed, 8193 insertions(+), 71 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v5 0/6] Link Bonding Library 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty @ 2014-06-18 16:18 ` Neil Horman 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty ` (6 subsequent siblings) 7 siblings, 0 replies; 127+ messages in thread From: Neil Horman @ 2014-06-18 16:18 UTC (permalink / raw) To: Declan Doherty; +Cc: dev On Wed, Jun 18, 2014 at 05:14:17PM +0100, Declan Doherty wrote: > This patch contains the initial release of the Link Bonding PMD Library > > Supporting bonding modes: > 0 - Round Robin > 1 - Active Backup > 2 - Balance (Supporting 3 transmission polices) > layer 2, layer 2+3, layer 3+4 > 3 - Broadcast > > Version 5 of patch set: > Contains changes to EAL code to allow initialisation of Bonded devices from > application startup options. rte_eal_init now calls rte_eal_pci_probe > between calling rte_eal_dev_init with PRE and POST PCI probe flags. This gets > around polluting the eal pci code with references to link bonding devices. > Also rte_eal_pci_probe can now be called multiple times and will not try to > re-initialize the driver if one already exists, this means that existing > applications which currently call rte_eal_pci_probe will not be affected > by this change > > > Patch Set Description: > 0001 - librte_pmd_bond + makefile changes > 0002 - librte_ether changes to support unique naming of pmds > 0003 - librte_eal changes to support bonding device intialization > 0005 - link bonding unti test suite > 0005 - testpmd link bonding support changes > 0006 - doxygen additions > > > Declan Doherty (6): > Link Bonding Library (lib/librte_pmd_bond) > Support for unique interface naming of pmds > EAL support for link bonding device initialization > Link bonding Unit Tests > testpmd link bonding additions > Link Bonding Library doxygen additions > > app/test-pmd/cmdline.c | 579 ++++ > app/test-pmd/config.c | 4 +- > app/test-pmd/parameters.c | 3 + > app/test-pmd/testpmd.c | 40 +- > app/test-pmd/testpmd.h | 2 + > app/test/Makefile | 4 +- > app/test/commands.c | 7 + > app/test/packet_burst_generator.c | 287 ++ > app/test/packet_burst_generator.h | 78 + > app/test/test.h | 1 + > app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ > app/test/virtual_pmd.c | 574 ++++ > app/test/virtual_pmd.h | 74 + > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_eal/bsdapp/eal/eal.c | 10 +- > lib/librte_eal/common/eal_common_dev.c | 58 +- > lib/librte_eal/common/eal_common_pci.c | 3 + > lib/librte_eal/common/include/eal_private.h | 7 - > lib/librte_eal/common/include/rte_dev.h | 13 +- > lib/librte_eal/linuxapp/eal/eal.c | 11 +- > lib/librte_ether/rte_ethdev.c | 32 +- > lib/librte_ether/rte_ethdev.h | 7 +- > lib/librte_pmd_bond/Makefile | 32 + > lib/librte_pmd_bond/rte_eth_bond.c | 2148 +++++++++++++++ > lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- > lib/librte_pmd_ring/rte_eth_ring.c | 32 +- > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > mk/rte.app.mk | 5 + > 34 files changed, 8193 insertions(+), 71 deletions(-) > create mode 100644 app/test/packet_burst_generator.c > create mode 100644 app/test/packet_burst_generator.h > create mode 100644 app/test/test_link_bonding.c > create mode 100644 app/test/virtual_pmd.c > create mode 100644 app/test/virtual_pmd.h > create mode 100644 lib/librte_pmd_bond/Makefile > create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c > create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h > > For the series Acked-by: Neil Horman <nhorman@tuxdriver.com> Thanks for all the hard work! Neil ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v6 0/6] Link Bonding Library 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty 2014-06-18 16:18 ` Neil Horman @ 2014-06-24 14:52 ` Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty ` (6 more replies) 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty ` (5 subsequent siblings) 7 siblings, 7 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 14:52 UTC (permalink / raw) To: dev This patch contains the initial release of the Link Bonding PMD Library Supporting bonding modes: 0 - Round Robin 1 - Active Backup 2 - Balance (Supporting 3 transmission polices) layer 2, layer 2+3, layer 3+4 3 - Broadcast Version 6 of patch set: Small fix for tx burst function in round robin mode which caused packets not to be distributed evenly across slaves if the burst sizes is continually 1. Patch Set Description: 0001 - librte_pmd_bond + makefile changes 0002 - librte_ether changes to support unique naming of pmds 0003 - librte_eal changes to support bonding device intialization 0005 - link bonding unti test suite 0005 - testpmd link bonding support changes 0006 - doxygen additions Declan Doherty (6): Link Bonding Library (lib/librte_pmd_bond) Support for unique interface naming of pmds EAL support for link bonding device initialization Link bonding Unit Tests testpmd link bonding additions Link Bonding Library doxygen additions app/test-pmd/cmdline.c | 579 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/bsdapp/eal/eal.c | 10 +- lib/librte_eal/common/eal_common_dev.c | 58 +- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 - lib/librte_eal/common/include/rte_dev.h | 13 +- lib/librte_eal/linuxapp/eal/eal.c | 11 +- lib/librte_ether/rte_ethdev.c | 32 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2142 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 5 + 34 files changed, 8187 insertions(+), 71 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v7 0/6] Link Bonding Library 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty @ 2014-06-24 16:03 ` Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 " Declan Doherty ` (6 more replies) 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty ` (5 subsequent siblings) 6 siblings, 7 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 16:03 UTC (permalink / raw) To: dev This patch contains the initial release of the Link Bonding PMD Library Supporting bonding modes: 0 - Round Robin 1 - Active Backup 2 - Balance (Supporting 3 transmission polices) layer 2, layer 2+3, layer 3+4 3 - Broadcast Version 6 of patch set: Debug tracing formatting issue in rte_eth_dev_allocate Patch Set Description: 0001 - librte_pmd_bond + makefile changes 0002 - librte_ether changes to support unique naming of pmds 0003 - librte_eal changes to support bonding device intialization 0005 - link bonding unti test suite 0005 - testpmd link bonding support changes 0006 - doxygen additions Declan Doherty (6): Link Bonding Library (lib/librte_pmd_bond) Support for unique interface naming of pmds EAL support for link bonding device initialization Link bonding Unit Tests testpmd link bonding additions Link Bonding Library doxygen additions app/test-pmd/cmdline.c | 579 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/bsdapp/eal/eal.c | 10 +- lib/librte_eal/common/eal_common_dev.c | 58 +- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 - lib/librte_eal/common/include/rte_dev.h | 13 +- lib/librte_eal/linuxapp/eal/eal.c | 11 +- lib/librte_ether/rte_ethdev.c | 33 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2142 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 5 + 34 files changed, 8188 insertions(+), 71 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v8 0/6] Link Bonding Library 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty @ 2014-06-25 20:07 ` Declan Doherty 2014-06-26 16:02 ` De Lara Guarch, Pablo 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty ` (5 subsequent siblings) 6 siblings, 2 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-25 20:07 UTC (permalink / raw) To: dev This patch contains the initial release of the Link Bonding PMD Library Supporting bonding modes: 0 - Round Robin 1 - Active Backup 2 - Balance (Supporting 3 transmission polices) layer 2, layer 2+3, layer 3+4 3 - Broadcast Version 8 of patch set: This version splits the bonding library into 3 C files, containing the PMD specific code, the argument parsing code, and the public API code Patch Set Description: 0001 - librte_pmd_bond + makefile changes 0002 - librte_ether changes to support unique naming of pmds 0003 - librte_eal changes to support bonding device intialization 0005 - link bonding unti test suite 0005 - testpmd link bonding support changes 0006 - doxygen additions Declan Doherty (6): Link Bonding Library (lib/librte_pmd_bond) Support for unique interface naming of pmds EAL support for link bonding device initialization Link bonding Unit Tests testpmd link bonding additions Link Bonding Library doxygen additions app/test-pmd/cmdline.c | 579 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/bsdapp/eal/eal.c | 10 +- lib/librte_eal/common/eal_common_dev.c | 58 +- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 - lib/librte_eal/common/include/rte_dev.h | 13 +- lib/librte_eal/linuxapp/eal/eal.c | 11 +- lib/librte_ether/rte_ethdev.c | 32 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 34 + lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_bond/rte_eth_bond_api.c | 670 +++++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1228 +++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 218 ++ lib/librte_pmd_bond/rte_eth_bond_vargs.c | 255 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 5 + 37 files changed, 8418 insertions(+), 71 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_vargs.c ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v8 0/6] Link Bonding Library 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 " Declan Doherty @ 2014-06-26 16:02 ` De Lara Guarch, Pablo 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon 1 sibling, 0 replies; 127+ messages in thread From: De Lara Guarch, Pablo @ 2014-06-26 16:02 UTC (permalink / raw) To: Doherty, Declan, dev > -----Original Message----- > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Declan Doherty > Sent: Wednesday, June 25, 2014 9:08 PM > To: dev@dpdk.org > Subject: [dpdk-dev] [PATCH v8 0/6] Link Bonding Library > > This patch contains the initial release of the Link Bonding PMD Library > > Supporting bonding modes: > 0 - Round Robin > 1 - Active Backup > 2 - Balance (Supporting 3 transmission polices) > layer 2, layer 2+3, layer 3+4 > 3 - Broadcast > > Version 8 of patch set: > This version splits the bonding library into 3 C files, containing the > PMD specific code, the argument parsing code, and the public API code > > > Patch Set Description: > 0001 - librte_pmd_bond + makefile changes > 0002 - librte_ether changes to support unique naming of pmds > 0003 - librte_eal changes to support bonding device intialization > 0005 - link bonding unti test suite > 0005 - testpmd link bonding support changes > 0006 - doxygen additions > > Declan Doherty (6): > Link Bonding Library (lib/librte_pmd_bond) > Support for unique interface naming of pmds > EAL support for link bonding device initialization > Link bonding Unit Tests > testpmd link bonding additions > Link Bonding Library doxygen additions > > app/test-pmd/cmdline.c | 579 ++++ > app/test-pmd/config.c | 4 +- > app/test-pmd/parameters.c | 3 + > app/test-pmd/testpmd.c | 40 +- > app/test-pmd/testpmd.h | 2 + > app/test/Makefile | 4 +- > app/test/commands.c | 7 + > app/test/packet_burst_generator.c | 287 ++ > app/test/packet_burst_generator.h | 78 + > app/test/test.h | 1 + > app/test/test_link_bonding.c | 3958 > +++++++++++++++++++++++++++ > app/test/virtual_pmd.c | 574 ++++ > app/test/virtual_pmd.h | 74 + > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_eal/bsdapp/eal/eal.c | 10 +- > lib/librte_eal/common/eal_common_dev.c | 58 +- > lib/librte_eal/common/eal_common_pci.c | 3 + > lib/librte_eal/common/include/eal_private.h | 7 - > lib/librte_eal/common/include/rte_dev.h | 13 +- > lib/librte_eal/linuxapp/eal/eal.c | 11 +- > lib/librte_ether/rte_ethdev.c | 32 +- > lib/librte_ether/rte_ethdev.h | 7 +- > lib/librte_pmd_bond/Makefile | 34 + > lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ > lib/librte_pmd_bond/rte_eth_bond_api.c | 670 +++++ > lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1228 +++++++++ > lib/librte_pmd_bond/rte_eth_bond_private.h | 218 ++ > lib/librte_pmd_bond/rte_eth_bond_vargs.c | 255 ++ > lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- > lib/librte_pmd_ring/rte_eth_ring.c | 32 +- > lib/librte_pmd_ring/rte_eth_ring.h | 3 +- > lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- > mk/rte.app.mk | 5 + > 37 files changed, 8418 insertions(+), 71 deletions(-) > create mode 100644 app/test/packet_burst_generator.c > create mode 100644 app/test/packet_burst_generator.h > create mode 100644 app/test/test_link_bonding.c > create mode 100644 app/test/virtual_pmd.c > create mode 100644 app/test/virtual_pmd.h > create mode 100644 lib/librte_pmd_bond/Makefile > create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_vargs.c Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v9 0/5] link bonding 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 " Declan Doherty 2014-06-26 16:02 ` De Lara Guarch, Pablo @ 2014-06-26 23:57 ` Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library Thomas Monjalon ` (10 more replies) 1 sibling, 11 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-26 23:57 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, I've made some adjustments in your patches. * Some renaming: - RTE_PMD_BOND / PMD_BOND_KVARG -> PMD_BOND_NAME - rte_pmd_bond_init -> bond_init - rte_pmd_bond_drv -> bond_drv - rte_eth_bond_vargs.c -> rte_eth_bond_args.c * Some moves in doxygen configuration * Removed rte_snprintf usages * Reword commit logs bond: new link bonding library ethdev: add unique name to devices eal: support link bonding device initialization bond: unit tests bond: testpmd support But there is still something wrong in the Makefile: the copyright is missing. I cannot add it by myself, so please send a v10 based on this v9 with copyright added. Thanks ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon @ 2014-06-26 23:57 ` Thomas Monjalon 2014-06-27 0:45 ` Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 2/5] ethdev: add unique name to devices Thomas Monjalon ` (9 subsequent siblings) 10 siblings, 1 reply; 127+ messages in thread From: Thomas Monjalon @ 2014-06-26 23:57 UTC (permalink / raw) To: Declan Doherty; +Cc: dev From: Declan Doherty <declan.doherty@intel.com> Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.h | 255 ++++++ lib/librte_pmd_bond/rte_eth_bond_api.c | 662 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_args.c | 252 ++++++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1212 ++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 215 +++++ mk/rte.app.mk | 4 + 12 files changed, 2645 insertions(+) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_args.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h diff --git a/config/common_bsdapp b/config/common_bsdapp index d5db4ab..c243b0c 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -206,6 +206,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding PMD library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index c5c0cb6..6237ce5 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -244,6 +244,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding PMD library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 7b26e98..d72659a 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -38,6 +38,7 @@ There are many libraries, so their headers may be grouped by topics: - **device**: [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), + [bond] (@ref rte_eth_bond.h), [KNI] (@ref rte_kni.h), [PCI] (@ref rte_pci.h), [PCI IDs] (@ref rte_pci_dev_ids.h) diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index f380d9a..1fd4492 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -47,6 +47,7 @@ INPUT = doc/doxy-api-index.md \ lib/librte_pipeline \ lib/librte_port \ lib/librte_power \ + lib/librte_pmd_bond \ lib/librte_ring \ lib/librte_sched \ lib/librte_table \ diff --git a/lib/Makefile b/lib/Makefile index c58c0c9..10c5bb3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -44,6 +44,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether DIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += librte_pmd_e1000 DIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += librte_pmd_ixgbe DIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += librte_pmd_i40e +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_PMD_RING) += librte_pmd_ring DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..6960551 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,32 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_api.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_pmd.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_vargs.c + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..95a4fa9 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/librte_pmd_bond/rte_eth_bond_api.c b/lib/librte_pmd_bond/rte_eth_bond_api.c new file mode 100644 index 0000000..9be5f72 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_api.c @@ -0,0 +1,662 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + +uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + +const char *driver_name = "Link Bonding PMD"; + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket\n"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket\n"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket\n"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket\n"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket\n"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev\n"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_pmd_bond/rte_eth_bond_args.c b/lib/librte_pmd_bond/rte_eth_bond_args.c new file mode 100644 index 0000000..11d9816 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_args.c @@ -0,0 +1,252 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_devargs.h> +#include <rte_kvargs.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + return port_id; +} + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} diff --git a/lib/librte_pmd_bond/rte_eth_bond_pmd.c b/lib/librte_pmd_bond/rte_eth_bond_pmd.c new file mode 100644 index 0000000..18fa54a --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_pmd.c @@ -0,0 +1,1212 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int slave_idx = 0; + int i, cs_idx = 0; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + cs_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cs_idx][(slave_nb_pkts[cs_idx])++] = bufs[i]; + } + + /* increment current slave index so the next call to tx burst starts on the + * next slave */ + slave_idx = ++cs_idx; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) + if (slave_nb_pkts[i] > 0) + num_tx_total += rte_eth_tx_burst(slaves[i], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + return -1; + } + } + } + } + + return 0; +} + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + internals->mode = mode; + + return 0; +} + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static int +bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver bond_drv = { + .name = PMD_BOND_NAME, + .type = PMD_BDEV, + .init = bond_init, +}; + +PMD_REGISTER_DRIVER(bond_drv); diff --git a/lib/librte_pmd_bond/rte_eth_bond_private.h b/lib/librte_pmd_bond/rte_eth_bond_private.h new file mode 100644 index 0000000..60f1e8d --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_private.h @@ -0,0 +1,215 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_PRIVATE_H_ +#define _RTE_ETH_BOND_PRIVATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ethdev.h> + +#include "rte_eth_bond.h" + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +extern const char *pmd_bond_init_valid_arguments[]; + +extern const char *driver_name; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +extern struct eth_dev_ops default_dev_ops; + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev); + +int +valid_port_id(uint8_t port_id); + +int +valid_bonded_port_id(uint8_t port_id); + +int +valid_slave_port_id(uint8_t port_id); + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link); +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev); + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link); + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr); + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev); + +uint8_t +number_of_sockets(void); + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode); + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id); + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param); + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 8db9cde..a4624e5 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -205,6 +205,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond +endif + endif LDLIBS += $(EXECENV_LDLIBS) -- 2.0.0 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library Thomas Monjalon @ 2014-06-27 0:45 ` Thomas Monjalon 0 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-27 0:45 UTC (permalink / raw) To: Declan Doherty; +Cc: dev 2014-06-27 01:57, Thomas Monjalon: > +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_vargs.c I forgot to rename the file here to rte_eth_bond_args.c. -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v9 2/5] ethdev: add unique name to devices 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library Thomas Monjalon @ 2014-06-26 23:57 ` Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 3/5] eal: support link bonding device initialization Thomas Monjalon ` (8 subsequent siblings) 10 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-26 23:57 UTC (permalink / raw) To: Declan Doherty; +Cc: dev From: Declan Doherty <declan.doherty@intel.com> Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- lib/librte_ether/rte_ethdev.c | 31 ++++++++++++++++++++++++++++--- lib/librte_ether/rte_ethdev.h | 7 ++++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +++++++++++----------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++++--------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 ++- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 8687499..a21cdb9 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -65,6 +65,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -153,21 +154,39 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static struct rte_eth_dev * +rte_eth_dev_allocated(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return &rte_eth_devices[i]; + } + return NULL; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; if (nb_ports == RTE_MAX_ETHPORTS) { - PMD_DEBUG_TRACE("Reached maximum number of ethernet ports\n"); + PMD_DEBUG_TRACE("Reached maximum number of Ethernet ports\n"); return NULL; } if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_allocated(name) != NULL) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } @@ -178,11 +197,17 @@ rte_eth_dev_init(struct rte_pci_driver *pci_drv, { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique Ethernet device name using PCI address */ + snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 526331a..25f2df7 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1497,6 +1497,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1505,6 +1507,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1560,10 +1564,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index ce7ad71..73c649e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 18c44f5..450332a 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 2.0.0 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v9 3/5] eal: support link bonding device initialization 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 2/5] ethdev: add unique name to devices Thomas Monjalon @ 2014-06-26 23:57 ` Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 4/5] bond: unit tests Thomas Monjalon ` (7 subsequent siblings) 10 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-26 23:57 UTC (permalink / raw) To: Declan Doherty; +Cc: dev From: Declan Doherty <declan.doherty@intel.com> Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- lib/librte_eal/bsdapp/eal/eal.c | 10 ++++- lib/librte_eal/common/eal_common_dev.c | 58 ++++++++++++++++++++--------- lib/librte_eal/common/eal_common_pci.c | 3 ++ lib/librte_eal/common/include/eal_private.h | 7 ---- lib/librte_eal/common/include/rte_dev.h | 14 ++++++- lib/librte_eal/linuxapp/eal/eal.c | 11 +++++- 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index ce0db03..ec57b26 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -919,7 +919,7 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -951,6 +951,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..1194419 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) } int -rte_eal_dev_init(void) +rte_eal_dev_init(uint8_t init_pri) { struct rte_devargs *devargs; struct rte_driver *driver; @@ -80,30 +80,52 @@ rte_eal_dev_init(void) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) - continue; + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, + * virtual devices are initialized pre PCI probing and bonded + * device are post pci probing */ + if ((driver->type == PMD_VDEV && init_pri == + PMD_INIT_PRE_PCI_PROBE) || + (driver->type == PMD_BDEV && init_pri == + PMD_INIT_POST_PCI_PROBE)) { - /* search a driver prefix in virtual device name */ - if (!strncmp(driver->name, devargs->virtual.drv_name, - strlen(driver->name))) { - driver->init(devargs->virtual.drv_name, - devargs->args); - break; + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + printf("init (%u) %s\n", init_pri, devargs->virtual.drv_name); + driver->init(devargs->virtual.drv_name, + devargs->args); + break; + } } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + /* If initializing pre PCI probe, then we don't expect a bonded driver + * to be found */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE && + strncmp(PMD_BOND_NAME, devargs->virtual.drv_name, + strlen(PMD_BOND_NAME)) != 0) { + if (driver == NULL) { + rte_panic("no driver found for virtual device %s\n", + devargs->virtual.drv_name); + } + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && + strncmp(PMD_BOND_NAME, devargs->virtual.drv_name, + strlen(PMD_BOND_NAME)) == 0) { + if (driver == NULL) { + rte_panic("no driver found for bonded device %s\n", + devargs->virtual.drv_name); + } } } - /* Once the vdevs are initalized, start calling all the pdev drivers */ - TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_PDEV) - continue; - /* PDEV drivers don't get passed any parameters */ - driver->init(NULL, NULL); + /* Once the vdevs are initialized, start calling all the pdev drivers */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_PDEV) + continue; + /* PDEV drivers don't get passed any parameters */ + driver->init(NULL, NULL); + } } return 0; } diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index af809a8..c637361 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -150,6 +150,9 @@ rte_eal_pci_probe(void) probe_all = 1; TAILQ_FOREACH(dev, &pci_device_list, next) { + /* check if device has already been initialized */ + if (dev->driver != NULL) + continue; /* set devargs in PCI structure */ devargs = pci_devargs_lookup(dev); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..b440ffb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -196,11 +196,4 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); -/** - * This function initialises any virtual devices - * - * This function is private to the EAL. - */ -int rte_eal_dev_init(void); - #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..d89f1a5 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,16 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ +}; + +#define PMD_BOND_NAME "eth_bond" + +/** + * Driver initialization */ +enum pmd_init_priority { + PMD_INIT_PRE_PCI_PROBE = 0, + PMD_INIT_POST_PCI_PROBE = 1, }; /** @@ -93,9 +103,9 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** - * Initalize all the registered drivers in this process + * Initialize all the registered drivers in this process */ -int rte_eal_dev_init(void); +int rte_eal_dev_init(uint8_t init_priority); #define PMD_REGISTER_DRIVER(d)\ void devinitfn_ ##d(void);\ diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index 00d74ed..d1c082d 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -75,6 +75,7 @@ #include <rte_atomic.h> #include <malloc_heap.h> #include <rte_eth_ring.h> +#include <rte_dev.h> #include "eal_private.h" #include "eal_thread.h" @@ -1142,7 +1143,7 @@ rte_eal_init(int argc, char **argv) RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", rte_config.master_lcore, (int)thread_id); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -1172,6 +1173,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } -- 2.0.0 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v9 4/5] bond: unit tests 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (2 preceding siblings ...) 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 3/5] eal: support link bonding device initialization Thomas Monjalon @ 2014-06-26 23:57 ` Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 5/5] bond: testpmd support Thomas Monjalon ` (6 subsequent siblings) 10 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-26 23:57 UTC (permalink / raw) To: Declan Doherty; +Cc: dev From: Declan Doherty <declan.doherty@intel.com> Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4982 insertions(+), 1 deletion(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 45d0cf2..cec2f2f 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -102,7 +102,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index c9dc085..5f23420 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -159,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -227,6 +231,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..5d539f1 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,287 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index c54e7ef..c00e1d6 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -97,6 +97,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..3f14278 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 2.0.0 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v9 5/5] bond: testpmd support 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (3 preceding siblings ...) 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 4/5] bond: unit tests Thomas Monjalon @ 2014-06-26 23:57 ` Thomas Monjalon 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty ` (5 subsequent siblings) 10 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-26 23:57 UTC (permalink / raw) To: Declan Doherty; +Cc: dev From: Declan Doherty <declan.doherty@intel.com> - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- app/test-pmd/cmdline.c | 579 ++++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 619 insertions(+), 9 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 45ce343..5cf93bb 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -404,6 +407,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -3031,6 +3059,547 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static int bond_dev_num = 0; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int port_id; + + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "eth_bond_testpmd_%d", + bond_dev_num++); + + /* Create a new bonded device. */ + port_id = rte_eth_bond_create(ethdev_name, res->mode, res->socket); + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("Created new bonded device %s on (port %d).\n", ethdev_name, + port_id); + + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -6572,6 +7141,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 32f2134..c72f6ee 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -252,6 +252,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -265,7 +266,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 4bca0b0..9573a43 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 16fe596..e8a4b45 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1252,7 +1278,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1380,10 +1406,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; @@ -1826,9 +1853,6 @@ main(int argc, char** argv) if (diag < 0) rte_panic("Cannot init EAL\n"); - if (rte_eal_pci_probe()) - rte_panic("Cannot probe PCI\n"); - nb_ports = (portid_t) rte_eth_dev_count(); if (nb_ports == 0) rte_exit(EXIT_FAILURE, "No probed ethernet devices - " diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index e263616..ac86bfe 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -281,6 +281,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -454,6 +455,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); void port_mtu_set(portid_t port_id, uint16_t mtu); -- 2.0.0 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v10 0/5] link bonding 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (4 preceding siblings ...) 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 5/5] bond: testpmd support Thomas Monjalon @ 2014-06-27 10:18 ` Declan Doherty 2014-06-27 20:58 ` Thomas Monjalon ` (6 more replies) 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 1/5] bond: new link bonding library Declan Doherty ` (4 subsequent siblings) 10 siblings, 7 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-27 10:18 UTC (permalink / raw) To: dev Fixed copyright in link bonding library Makefile Declan Doherty (5): bond: new link bonding library ethdev: add unique name to devices eal: support link bonding device initialization bond: unit tests bond: testpmd support app/test-pmd/cmdline.c | 579 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/bsdapp/eal/eal.c | 10 +- lib/librte_eal/common/eal_common_dev.c | 58 +- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 - lib/librte_eal/common/include/rte_dev.h | 14 +- lib/librte_eal/linuxapp/eal/eal.c | 11 +- lib/librte_ether/rte_ethdev.c | 31 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 61 + lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_bond/rte_eth_bond_api.c | 662 +++++ lib/librte_pmd_bond/rte_eth_bond_args.c | 252 ++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1212 ++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 215 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 4 + 37 files changed, 8414 insertions(+), 71 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_args.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v10 0/5] link bonding 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty @ 2014-06-27 20:58 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 0/5] link bonding library Declan Doherty ` (5 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-27 20:58 UTC (permalink / raw) To: Declan Doherty; +Cc: dev 2014-06-27 11:18, Declan Doherty: > Fixed copyright in link bonding library Makefile A small fix is needed in Makefile for renaming of file rte_eth_bond_args.c. And more important, it cannot be built as an optional shared library as test applications call functions from the specific API. The best design would be to have a base library always linked and an optional PMD layer. -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v11 0/5] link bonding library 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty 2014-06-27 20:58 ` Thomas Monjalon @ 2014-06-29 17:49 ` Declan Doherty 2014-06-30 9:21 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 1/5] bond: new " Declan Doherty ` (4 subsequent siblings) 6 siblings, 1 reply; 127+ messages in thread From: Declan Doherty @ 2014-06-29 17:49 UTC (permalink / raw) To: dev Hi Thomas, This patchset contains the name change fix in the link bonding library makefile. I have also put a change into the unit test application makefile so that the link bonding tests are not built if the library is also not being built. It doesn't make sense to split the bonding libraries APIs into a seperate library, as they are directly coupled to with the bonding library implmentation Declan Doherty (5): bond: new link bonding library ethdev: add unique name to devices eal: support link bonding device initialization bond: unit tests bond: testpmd support app/test-pmd/cmdline.c | 579 ++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +- app/test-pmd/testpmd.h | 2 + app/test/Makefile | 6 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 ++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++ app/test/virtual_pmd.h | 74 + config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_eal/bsdapp/eal/eal.c | 10 +- lib/librte_eal/common/eal_common_dev.c | 58 +- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 - lib/librte_eal/common/include/rte_dev.h | 14 +- lib/librte_eal/linuxapp/eal/eal.c | 11 +- lib/librte_ether/rte_ethdev.c | 31 +- lib/librte_ether/rte_ethdev.h | 7 +- lib/librte_pmd_bond/Makefile | 61 + lib/librte_pmd_bond/rte_eth_bond.h | 255 ++ lib/librte_pmd_bond/rte_eth_bond_api.c | 662 +++++ lib/librte_pmd_bond/rte_eth_bond_args.c | 252 ++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1212 ++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 215 ++ lib/librte_pmd_pcap/rte_eth_pcap.c | 22 +- lib/librte_pmd_ring/rte_eth_ring.c | 32 +- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- mk/rte.app.mk | 4 + 37 files changed, 8416 insertions(+), 71 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_args.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 0/5] link bonding library 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 0/5] link bonding library Declan Doherty @ 2014-06-30 9:21 ` Thomas Monjalon 2014-06-30 9:28 ` Doherty, Declan 0 siblings, 1 reply; 127+ messages in thread From: Thomas Monjalon @ 2014-06-30 9:21 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, 2014-06-29 18:49, Declan Doherty: > This patchset contains the name change fix in the link bonding library > makefile. I have also put a change into the unit test application makefile > so that the link bonding tests are not built if the library is also not > being built. It doesn't make sense to split the bonding libraries APIs into > a seperate library, as they are directly coupled to with the bonding > library implmentation You fixed optional building but there is still an issue when building shared library. As you don't want to split the library to separate the PMD plugin from the specific API, I suggest to not consider it as a plugin (unlike other PMDs). I've sent some patches about these 2 topics. If you agree, I'll apply them, merged with your serie. Thanks -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 0/5] link bonding library 2014-06-30 9:21 ` Thomas Monjalon @ 2014-06-30 9:28 ` Doherty, Declan 2014-07-01 22:01 ` Thomas Monjalon 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-30 9:28 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev > -----Original Message----- > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > Sent: Monday, June 30, 2014 10:21 AM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [PATCH v11 0/5] link bonding library > > Hi Declan, > > 2014-06-29 18:49, Declan Doherty: > > This patchset contains the name change fix in the link bonding library > > makefile. I have also put a change into the unit test application makefile > > so that the link bonding tests are not built if the library is also not > > being built. It doesn't make sense to split the bonding libraries APIs into > > a seperate library, as they are directly coupled to with the bonding > > library implmentation > > You fixed optional building but there is still an issue when building shared > library. > As you don't want to split the library to separate the PMD plugin from the > specific API, I suggest to not consider it as a plugin (unlike other PMDs). > > I've sent some patches about these 2 topics. > If you agree, I'll apply them, merged with your serie. > > Thanks > -- > Thomas Hi Thomas, I'm happy for you to apply the makefile changes you have suggested and not consider the bonding PMD as a plugin, I think this makes the most sense considering it is a purely software library. Thanks Declan ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 0/5] link bonding library 2014-06-30 9:28 ` Doherty, Declan @ 2014-07-01 22:01 ` Thomas Monjalon 0 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-07-01 22:01 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev 2014-06-30 09:28, Doherty, Declan: > > -----Original Message----- > > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > > > > Hi Declan, > > > > 2014-06-29 18:49, Declan Doherty: > > > This patchset contains the name change fix in the link bonding library > > > makefile. I have also put a change into the unit test application > > > makefile > > > so that the link bonding tests are not built if the library is also not > > > being built. It doesn't make sense to split the bonding libraries APIs > > > into > > > a seperate library, as they are directly coupled to with the bonding > > > library implmentation > > > > You fixed optional building but there is still an issue when building > > shared library. > > As you don't want to split the library to separate the PMD plugin from the > > specific API, I suggest to not consider it as a plugin (unlike other > > PMDs). > > > > I've sent some patches about these 2 topics. > > If you agree, I'll apply them, merged with your serie. > > I'm happy for you to apply the makefile changes you have suggested and not > consider the bonding PMD as a plugin, I think this makes the most sense > considering it is a purely software library. Applied with Robert's changes for version 1.7.0. Thanks for all these revisions (v11 is currently the best score :) -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v11 1/5] bond: new link bonding library 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty 2014-06-27 20:58 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 0/5] link bonding library Declan Doherty @ 2014-06-29 17:49 ` Declan Doherty 2014-06-30 9:13 ` Thomas Monjalon 2014-06-30 22:29 ` Robert Sanford 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 2/5] ethdev: add unique name to devices Declan Doherty ` (3 subsequent siblings) 6 siblings, 2 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-29 17:49 UTC (permalink / raw) To: dev Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 61 ++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++++++ lib/librte_pmd_bond/rte_eth_bond_api.c | 662 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_args.c | 252 ++++++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1212 ++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 215 +++++ mk/rte.app.mk | 4 + 12 files changed, 2674 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_args.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h diff --git a/config/common_bsdapp b/config/common_bsdapp index d5db4ab..c243b0c 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -206,6 +206,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding PMD library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index c5c0cb6..6237ce5 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -244,6 +244,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding PMD library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 7b26e98..d72659a 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -38,6 +38,7 @@ There are many libraries, so their headers may be grouped by topics: - **device**: [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), + [bond] (@ref rte_eth_bond.h), [KNI] (@ref rte_kni.h), [PCI] (@ref rte_pci.h), [PCI IDs] (@ref rte_pci_dev_ids.h) diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index f380d9a..1fd4492 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -47,6 +47,7 @@ INPUT = doc/doxy-api-index.md \ lib/librte_pipeline \ lib/librte_port \ lib/librte_power \ + lib/librte_pmd_bond \ lib/librte_ring \ lib/librte_sched \ lib/librte_table \ diff --git a/lib/Makefile b/lib/Makefile index c58c0c9..10c5bb3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -44,6 +44,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether DIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += librte_pmd_e1000 DIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += librte_pmd_ixgbe DIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += librte_pmd_i40e +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_PMD_RING) += librte_pmd_ring DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..953d75e --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,61 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_api.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_pmd.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_args.c + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..95a4fa9 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/librte_pmd_bond/rte_eth_bond_api.c b/lib/librte_pmd_bond/rte_eth_bond_api.c new file mode 100644 index 0000000..9be5f72 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_api.c @@ -0,0 +1,662 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + +uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + +const char *driver_name = "Link Bonding PMD"; + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket\n"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket\n"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket\n"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket\n"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket\n"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev\n"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_pmd_bond/rte_eth_bond_args.c b/lib/librte_pmd_bond/rte_eth_bond_args.c new file mode 100644 index 0000000..11d9816 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_args.c @@ -0,0 +1,252 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_devargs.h> +#include <rte_kvargs.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + return port_id; +} + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} diff --git a/lib/librte_pmd_bond/rte_eth_bond_pmd.c b/lib/librte_pmd_bond/rte_eth_bond_pmd.c new file mode 100644 index 0000000..18fa54a --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_pmd.c @@ -0,0 +1,1212 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int slave_idx = 0; + int i, cs_idx = 0; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + cs_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cs_idx][(slave_nb_pkts[cs_idx])++] = bufs[i]; + } + + /* increment current slave index so the next call to tx burst starts on the + * next slave */ + slave_idx = ++cs_idx; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) + if (slave_nb_pkts[i] > 0) + num_tx_total += rte_eth_tx_burst(slaves[i], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + return -1; + } + } + } + } + + return 0; +} + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + internals->mode = mode; + + return 0; +} + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static int +bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver bond_drv = { + .name = PMD_BOND_NAME, + .type = PMD_BDEV, + .init = bond_init, +}; + +PMD_REGISTER_DRIVER(bond_drv); diff --git a/lib/librte_pmd_bond/rte_eth_bond_private.h b/lib/librte_pmd_bond/rte_eth_bond_private.h new file mode 100644 index 0000000..60f1e8d --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_private.h @@ -0,0 +1,215 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_PRIVATE_H_ +#define _RTE_ETH_BOND_PRIVATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ethdev.h> + +#include "rte_eth_bond.h" + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +extern const char *pmd_bond_init_valid_arguments[]; + +extern const char *driver_name; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +extern struct eth_dev_ops default_dev_ops; + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev); + +int +valid_port_id(uint8_t port_id); + +int +valid_bonded_port_id(uint8_t port_id); + +int +valid_slave_port_id(uint8_t port_id); + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link); +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev); + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link); + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr); + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev); + +uint8_t +number_of_sockets(void); + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode); + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id); + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param); + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 8db9cde..a4624e5 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -205,6 +205,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond +endif + endif LDLIBS += $(EXECENV_LDLIBS) -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 1/5] bond: new link bonding library 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 1/5] bond: new " Declan Doherty @ 2014-06-30 9:13 ` Thomas Monjalon 2014-06-30 22:29 ` Robert Sanford 1 sibling, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-30 9:13 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, 2014-06-29 18:49, Declan Doherty: > --- a/mk/rte.app.mk > +++ b/mk/rte.app.mk > @@ -205,6 +205,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) > LDLIBS += -lrte_pmd_pcap -lpcap > endif > > +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) > +LDLIBS += -lrte_pmd_bond > +endif > + > endif This part is linking bonding library only in case of static libraries. But this library has a specific API. So it cannot be a plugin and must always be linked. That's why I suggested to split the library. Instead, I suggest to always link with bonding library. See my proposal below: --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -175,7 +175,12 @@ LDLIBS += -lrte_pmd_xenvirt LDLIBS += -lxenstore endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond +endif + ifeq ($(CONFIG_RTE_BUILD_SHARED_LIB),n) +# plugins (link only if static libraries) ifeq ($(CONFIG_RTE_LIBRTE_VMXNET3_PMD),y) LDLIBS += -lrte_pmd_vmxnet3_uio @@ -205,7 +210,7 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif -endif +endif # plugins (link only if static libraries) Adding comments to explain plugin section will be clearer. -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 1/5] bond: new link bonding library 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 1/5] bond: new " Declan Doherty 2014-06-30 9:13 ` Thomas Monjalon @ 2014-06-30 22:29 ` Robert Sanford 2014-07-01 14:16 ` Thomas Monjalon 2014-07-01 14:19 ` Doherty, Declan 1 sibling, 2 replies; 127+ messages in thread From: Robert Sanford @ 2014-06-30 22:29 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, On Sun, Jun 29, 2014 at 1:49 PM, Declan Doherty <declan.doherty@intel.com> wrote: > Initial release with support for > Mode 0 - Round Robin > Mode 1 - Active Backup > Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, > layer 3+4) > Mode 3 - Broadcast > > Signed-off-by: Declan Doherty <declan.doherty@intel.com> > --- > config/common_bsdapp | 5 + > config/common_linuxapp | 5 + > doc/doxy-api-index.md | 1 + > doc/doxy-api.conf | 1 + > lib/Makefile | 1 + > lib/librte_pmd_bond/Makefile | 61 ++ > lib/librte_pmd_bond/rte_eth_bond.h | 255 ++++++ > lib/librte_pmd_bond/rte_eth_bond_api.c | 662 +++++++++++++++ > lib/librte_pmd_bond/rte_eth_bond_args.c | 252 ++++++ > lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1212 > ++++++++++++++++++++++++++++ > lib/librte_pmd_bond/rte_eth_bond_private.h | 215 +++++ > mk/rte.app.mk | 4 + > 12 files changed, 2674 insertions(+), 0 deletions(-) > create mode 100644 lib/librte_pmd_bond/Makefile > create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_args.c > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c > create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h > > > I see a potential problem with bond_ethdev_rx_burst( ). We could receive more packets than the caller asked for, and overrun the caller's rte_mbuf * array. The fix could be something like this: --- a/lib/librte_pmd_bond/rte_eth_bond_pmd.c +++ b/lib/librte_pmd_bond/rte_eth_bond_pmd.c @@ -73,13 +73,15 @@ bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) case BONDING_MODE_ROUND_ROBIN: case BONDING_MODE_BROADCAST: case BONDING_MODE_BALANCE: - for (i = 0; i < internals->active_slave_count; i++) { + for (i = 0; i < internals->active_slave_count && nb_pkts; i++) { /* Offset of pointer to *bufs increases as packets are received * from other slaves */ num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); - if (num_rx_slave) + if (num_rx_slave) { num_rx_total += num_rx_slave; + nb_pkts -= num_rx_slave; + } } break; case BONDING_MODE_ACTIVE_BACKUP: -- Regards, Robert ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 1/5] bond: new link bonding library 2014-06-30 22:29 ` Robert Sanford @ 2014-07-01 14:16 ` Thomas Monjalon 2014-07-01 14:19 ` Doherty, Declan 1 sibling, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-07-01 14:16 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, Do you confirm we should merge the Robert's patch in rc3? 2014-06-30 18:29, Robert Sanford: > I see a potential problem with bond_ethdev_rx_burst( ). > We could receive more packets than the caller asked for, and overrun the > caller's rte_mbuf * array. > The fix could be something like this: > > --- a/lib/librte_pmd_bond/rte_eth_bond_pmd.c > +++ b/lib/librte_pmd_bond/rte_eth_bond_pmd.c > @@ -73,13 +73,15 @@ bond_ethdev_rx_burst(void *queue, struct rte_mbuf > **bufs, uint16_t nb_pkts) > case BONDING_MODE_ROUND_ROBIN: > case BONDING_MODE_BROADCAST: > case BONDING_MODE_BALANCE: > - for (i = 0; i < internals->active_slave_count; i++) { > + for (i = 0; i < internals->active_slave_count && nb_pkts; > i++) { > /* Offset of pointer to *bufs increases as packets > are received > * from other slaves */ > num_rx_slave = > rte_eth_rx_burst(internals->active_slaves[i], > bd_rx_q->queue_id, bufs + > num_rx_total, nb_pkts); > - if (num_rx_slave) > + if (num_rx_slave) { > num_rx_total += num_rx_slave; > + nb_pkts -= num_rx_slave; > + } > } > break; > case BONDING_MODE_ACTIVE_BACKUP: ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 1/5] bond: new link bonding library 2014-06-30 22:29 ` Robert Sanford 2014-07-01 14:16 ` Thomas Monjalon @ 2014-07-01 14:19 ` Doherty, Declan 2014-07-01 14:26 ` Thomas Monjalon 1 sibling, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-07-01 14:19 UTC (permalink / raw) To: Robert Sanford; +Cc: dev From: Robert Sanford [mailto:rsanford2@gmail.com] Sent: Monday, June 30, 2014 11:30 PM To: Doherty, Declan Cc: dev@dpdk.org Subject: Re: [dpdk-dev] [PATCH v11 1/5] bond: new link bonding library Hi Declan, On Sun, Jun 29, 2014 at 1:49 PM, Declan Doherty <declan.doherty@intel.com<mailto:declan.doherty@intel.com>> wrote: Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com<mailto:declan.doherty@intel.com>> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md<http://doxy-api-index.md> | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 61 ++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++++++ lib/librte_pmd_bond/rte_eth_bond_api.c | 662 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_args.c | 252 ++++++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1212 ++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 215 +++++ mk/rte.app.mk<http://rte.app.mk> | 4 + 12 files changed, 2674 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_args.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h I see a potential problem with bond_ethdev_rx_burst( ). We could receive more packets than the caller asked for, and overrun the caller's rte_mbuf * array. The fix could be something like this: --- a/lib/librte_pmd_bond/rte_eth_bond_pmd.c +++ b/lib/librte_pmd_bond/rte_eth_bond_pmd.c @@ -73,13 +73,15 @@ bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) case BONDING_MODE_ROUND_ROBIN: case BONDING_MODE_BROADCAST: case BONDING_MODE_BALANCE: - for (i = 0; i < internals->active_slave_count; i++) { + for (i = 0; i < internals->active_slave_count && nb_pkts; i++) { /* Offset of pointer to *bufs increases as packets are received * from other slaves */ num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); - if (num_rx_slave) + if (num_rx_slave) { num_rx_total += num_rx_slave; + nb_pkts -= num_rx_slave; + } } break; case BONDING_MODE_ACTIVE_BACKUP: -- Regards, Robert Hi Robert, yes I see this could be an issue, although this currently shouldn’t cause an issue, as the currently supported bonding modes only expected to receive data on one slave at any time, if they are being used as part of a ether channel but when we add further bonding modes are which receive data on multiple slaves this would definitely be a problem. Thomas, I’ve tested the above changes, is it possible to integrate this patch with the patchset for link bonding for the rc3. Thanks Declan ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 1/5] bond: new link bonding library 2014-07-01 14:19 ` Doherty, Declan @ 2014-07-01 14:26 ` Thomas Monjalon 0 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-07-01 14:26 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev 2014-07-01 14:19, Doherty, Declan: > Hi Robert, yes I see this could be an issue, although this currently > shouldn’t cause an issue, as the currently supported bonding modes only > expected to receive data on one slave at any time, if they are being used > as part of a ether channel but when we add further bonding modes are which > receive data on multiple slaves this would definitely be a problem. > > Thomas, I’ve tested the above changes, is it possible to integrate this > patch with the patchset for link bonding for the rc3. No problem. It will be in. Thanks Declan & Robert -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v11 2/5] ethdev: add unique name to devices 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty ` (2 preceding siblings ...) 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 1/5] bond: new " Declan Doherty @ 2014-06-29 17:49 ` Declan Doherty 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 3/5] eal: support link bonding device initialization Declan Doherty ` (2 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-29 17:49 UTC (permalink / raw) To: dev Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- lib/librte_ether/rte_ethdev.c | 31 ++++++++++++++++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 +++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 ++++++++++---------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++-------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index ef81a76..0ebb8fb 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -65,6 +65,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -153,21 +154,39 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static struct rte_eth_dev * +rte_eth_dev_allocated(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return &rte_eth_devices[i]; + } + return NULL; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; if (nb_ports == RTE_MAX_ETHPORTS) { - PMD_DEBUG_TRACE("Reached maximum number of ethernet ports\n"); + PMD_DEBUG_TRACE("Reached maximum number of Ethernet ports\n"); return NULL; } if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_allocated(name) != NULL) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } @@ -178,11 +197,17 @@ rte_eth_dev_init(struct rte_pci_driver *pci_drv, { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique Ethernet device name using PCI address */ + snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 2406e45..50df654 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1497,6 +1497,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1505,6 +1507,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1560,10 +1564,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index ce7ad71..73c649e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 18c44f5..450332a 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v11 3/5] eal: support link bonding device initialization 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty ` (3 preceding siblings ...) 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 2/5] ethdev: add unique name to devices Declan Doherty @ 2014-06-29 17:49 ` Declan Doherty 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 4/5] bond: unit tests Declan Doherty 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 5/5] bond: testpmd support Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-29 17:49 UTC (permalink / raw) To: dev Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- lib/librte_eal/bsdapp/eal/eal.c | 10 ++++- lib/librte_eal/common/eal_common_dev.c | 58 ++++++++++++++++++-------- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 --- lib/librte_eal/common/include/rte_dev.h | 14 ++++++- lib/librte_eal/linuxapp/eal/eal.c | 11 +++++- 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index a1f014f..c53f63e 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -874,7 +874,7 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -906,6 +906,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..1194419 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) } int -rte_eal_dev_init(void) +rte_eal_dev_init(uint8_t init_pri) { struct rte_devargs *devargs; struct rte_driver *driver; @@ -80,30 +80,52 @@ rte_eal_dev_init(void) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) - continue; + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, + * virtual devices are initialized pre PCI probing and bonded + * device are post pci probing */ + if ((driver->type == PMD_VDEV && init_pri == + PMD_INIT_PRE_PCI_PROBE) || + (driver->type == PMD_BDEV && init_pri == + PMD_INIT_POST_PCI_PROBE)) { - /* search a driver prefix in virtual device name */ - if (!strncmp(driver->name, devargs->virtual.drv_name, - strlen(driver->name))) { - driver->init(devargs->virtual.drv_name, - devargs->args); - break; + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + printf("init (%u) %s\n", init_pri, devargs->virtual.drv_name); + driver->init(devargs->virtual.drv_name, + devargs->args); + break; + } } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + /* If initializing pre PCI probe, then we don't expect a bonded driver + * to be found */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE && + strncmp(PMD_BOND_NAME, devargs->virtual.drv_name, + strlen(PMD_BOND_NAME)) != 0) { + if (driver == NULL) { + rte_panic("no driver found for virtual device %s\n", + devargs->virtual.drv_name); + } + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && + strncmp(PMD_BOND_NAME, devargs->virtual.drv_name, + strlen(PMD_BOND_NAME)) == 0) { + if (driver == NULL) { + rte_panic("no driver found for bonded device %s\n", + devargs->virtual.drv_name); + } } } - /* Once the vdevs are initalized, start calling all the pdev drivers */ - TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_PDEV) - continue; - /* PDEV drivers don't get passed any parameters */ - driver->init(NULL, NULL); + /* Once the vdevs are initialized, start calling all the pdev drivers */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_PDEV) + continue; + /* PDEV drivers don't get passed any parameters */ + driver->init(NULL, NULL); + } } return 0; } diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index af809a8..c637361 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -150,6 +150,9 @@ rte_eal_pci_probe(void) probe_all = 1; TAILQ_FOREACH(dev, &pci_device_list, next) { + /* check if device has already been initialized */ + if (dev->driver != NULL) + continue; /* set devargs in PCI structure */ devargs = pci_devargs_lookup(dev); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..b440ffb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -196,11 +196,4 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); -/** - * This function initialises any virtual devices - * - * This function is private to the EAL. - */ -int rte_eal_dev_init(void); - #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..d89f1a5 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,16 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ +}; + +#define PMD_BOND_NAME "eth_bond" + +/** + * Driver initialization */ +enum pmd_init_priority { + PMD_INIT_PRE_PCI_PROBE = 0, + PMD_INIT_POST_PCI_PROBE = 1, }; /** @@ -93,9 +103,9 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** - * Initalize all the registered drivers in this process + * Initialize all the registered drivers in this process */ -int rte_eal_dev_init(void); +int rte_eal_dev_init(uint8_t init_priority); #define PMD_REGISTER_DRIVER(d)\ void devinitfn_ ##d(void);\ diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index d204387..573fd06 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -75,6 +75,7 @@ #include <rte_atomic.h> #include <malloc_heap.h> #include <rte_eth_ring.h> +#include <rte_dev.h> #include "eal_private.h" #include "eal_thread.h" @@ -1097,7 +1098,7 @@ rte_eal_init(int argc, char **argv) RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", rte_config.master_lcore, (int)thread_id); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -1127,6 +1128,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v11 4/5] bond: unit tests 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty ` (4 preceding siblings ...) 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 3/5] eal: support link bonding device initialization Declan Doherty @ 2014-06-29 17:49 ` Declan Doherty 2014-06-30 8:56 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 5/5] bond: testpmd support Declan Doherty 6 siblings, 1 reply; 127+ messages in thread From: Declan Doherty @ 2014-06-29 17:49 UTC (permalink / raw) To: dev Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- app/test/Makefile | 6 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4984 insertions(+), 1 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 45d0cf2..e1b2c03 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -102,7 +102,11 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c +endif ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index c9dc085..5f23420 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -159,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -227,6 +231,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..5d539f1 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,287 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index c54e7ef..c00e1d6 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -97,6 +97,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..3f14278 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v11 4/5] bond: unit tests 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 4/5] bond: unit tests Declan Doherty @ 2014-06-30 8:56 ` Thomas Monjalon 0 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-30 8:56 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, 2014-06-29 18:49, Declan Doherty: > +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) > +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c > +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c > +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c > +endif > ifeq ($(CONFIG_RTE_APP_TEST),y) > SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c > SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c I think there is a cleaner way to fix build without bonding. See my patch proposal below: SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c -SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += test_link_bonding.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v11 5/5] bond: testpmd support 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty ` (5 preceding siblings ...) 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 4/5] bond: unit tests Declan Doherty @ 2014-06-29 17:49 ` Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-29 17:49 UTC (permalink / raw) To: dev - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- app/test-pmd/cmdline.c | 579 +++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 619 insertions(+), 9 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index f61a31c..345be11 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -412,6 +415,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -3039,6 +3067,547 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static int bond_dev_num = 0; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int port_id; + + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "eth_bond_testpmd_%d", + bond_dev_num++); + + /* Create a new bonded device. */ + port_id = rte_eth_bond_create(ethdev_name, res->mode, res->socket); + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("Created new bonded device %s on (port %d).\n", ethdev_name, + port_id); + + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -6768,6 +7337,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 32f2134..c72f6ee 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -252,6 +252,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -265,7 +266,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 4bca0b0..9573a43 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 16fe596..e8a4b45 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1252,7 +1278,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1380,10 +1406,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; @@ -1826,9 +1853,6 @@ main(int argc, char** argv) if (diag < 0) rte_panic("Cannot init EAL\n"); - if (rte_eal_pci_probe()) - rte_panic("Cannot probe PCI\n"); - nb_ports = (portid_t) rte_eth_dev_count(); if (nb_ports == 0) rte_exit(EXIT_FAILURE, "No probed ethernet devices - " diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index e263616..ac86bfe 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -281,6 +281,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -454,6 +455,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); void port_mtu_set(portid_t port_id, uint16_t mtu); -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v10 1/5] bond: new link bonding library 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (5 preceding siblings ...) 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty @ 2014-06-27 10:18 ` Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 2/5] ethdev: add unique name to devices Declan Doherty ` (3 subsequent siblings) 10 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-27 10:18 UTC (permalink / raw) To: dev Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 61 ++ lib/librte_pmd_bond/rte_eth_bond.h | 255 ++++++ lib/librte_pmd_bond/rte_eth_bond_api.c | 662 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_args.c | 252 ++++++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1212 ++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 215 +++++ mk/rte.app.mk | 4 + 12 files changed, 2674 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_args.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h diff --git a/config/common_bsdapp b/config/common_bsdapp index d5db4ab..c243b0c 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -206,6 +206,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding PMD library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index c5c0cb6..6237ce5 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -244,6 +244,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding PMD library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 7b26e98..d72659a 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -38,6 +38,7 @@ There are many libraries, so their headers may be grouped by topics: - **device**: [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), + [bond] (@ref rte_eth_bond.h), [KNI] (@ref rte_kni.h), [PCI] (@ref rte_pci.h), [PCI IDs] (@ref rte_pci_dev_ids.h) diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index f380d9a..1fd4492 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -47,6 +47,7 @@ INPUT = doc/doxy-api-index.md \ lib/librte_pipeline \ lib/librte_port \ lib/librte_power \ + lib/librte_pmd_bond \ lib/librte_ring \ lib/librte_sched \ lib/librte_table \ diff --git a/lib/Makefile b/lib/Makefile index c58c0c9..10c5bb3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -44,6 +44,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether DIRS-$(CONFIG_RTE_LIBRTE_E1000_PMD) += librte_pmd_e1000 DIRS-$(CONFIG_RTE_LIBRTE_IXGBE_PMD) += librte_pmd_ixgbe DIRS-$(CONFIG_RTE_LIBRTE_I40E_PMD) += librte_pmd_i40e +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_PMD_RING) += librte_pmd_ring DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..21998f7 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,61 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2014 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_api.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_pmd.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_vargs.c + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..95a4fa9 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/librte_pmd_bond/rte_eth_bond_api.c b/lib/librte_pmd_bond/rte_eth_bond_api.c new file mode 100644 index 0000000..9be5f72 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_api.c @@ -0,0 +1,662 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + +uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + +const char *driver_name = "Link Bonding PMD"; + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket\n"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket\n"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket\n"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket\n"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket\n"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev\n"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_pmd_bond/rte_eth_bond_args.c b/lib/librte_pmd_bond/rte_eth_bond_args.c new file mode 100644 index 0000000..11d9816 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_args.c @@ -0,0 +1,252 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_devargs.h> +#include <rte_kvargs.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + return port_id; +} + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} diff --git a/lib/librte_pmd_bond/rte_eth_bond_pmd.c b/lib/librte_pmd_bond/rte_eth_bond_pmd.c new file mode 100644 index 0000000..18fa54a --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_pmd.c @@ -0,0 +1,1212 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int slave_idx = 0; + int i, cs_idx = 0; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + cs_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cs_idx][(slave_nb_pkts[cs_idx])++] = bufs[i]; + } + + /* increment current slave index so the next call to tx burst starts on the + * next slave */ + slave_idx = ++cs_idx; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) + if (slave_nb_pkts[i] > 0) + num_tx_total += rte_eth_tx_burst(slaves[i], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + return -1; + } + } + } + } + + return 0; +} + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + internals->mode = mode; + + return 0; +} + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static int +bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver bond_drv = { + .name = PMD_BOND_NAME, + .type = PMD_BDEV, + .init = bond_init, +}; + +PMD_REGISTER_DRIVER(bond_drv); diff --git a/lib/librte_pmd_bond/rte_eth_bond_private.h b/lib/librte_pmd_bond/rte_eth_bond_private.h new file mode 100644 index 0000000..60f1e8d --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_private.h @@ -0,0 +1,215 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_PRIVATE_H_ +#define _RTE_ETH_BOND_PRIVATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ethdev.h> + +#include "rte_eth_bond.h" + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +extern const char *pmd_bond_init_valid_arguments[]; + +extern const char *driver_name; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +extern struct eth_dev_ops default_dev_ops; + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev); + +int +valid_port_id(uint8_t port_id); + +int +valid_bonded_port_id(uint8_t port_id); + +int +valid_slave_port_id(uint8_t port_id); + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link); +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev); + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link); + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr); + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev); + +uint8_t +number_of_sockets(void); + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode); + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id); + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param); + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 8db9cde..a4624e5 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -205,6 +205,10 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond +endif + endif LDLIBS += $(EXECENV_LDLIBS) -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v10 2/5] ethdev: add unique name to devices 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (6 preceding siblings ...) 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 1/5] bond: new link bonding library Declan Doherty @ 2014-06-27 10:18 ` Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 3/5] eal: support link bonding device initialization Declan Doherty ` (2 subsequent siblings) 10 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-27 10:18 UTC (permalink / raw) To: dev Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- lib/librte_ether/rte_ethdev.c | 31 ++++++++++++++++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 +++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 ++++++++++---------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++-------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index ef81a76..0ebb8fb 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -65,6 +65,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -153,21 +154,39 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static struct rte_eth_dev * +rte_eth_dev_allocated(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return &rte_eth_devices[i]; + } + return NULL; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; if (nb_ports == RTE_MAX_ETHPORTS) { - PMD_DEBUG_TRACE("Reached maximum number of ethernet ports\n"); + PMD_DEBUG_TRACE("Reached maximum number of Ethernet ports\n"); return NULL; } if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_allocated(name) != NULL) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } @@ -178,11 +197,17 @@ rte_eth_dev_init(struct rte_pci_driver *pci_drv, { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique Ethernet device name using PCI address */ + snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 2406e45..50df654 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1497,6 +1497,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1505,6 +1507,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1560,10 +1564,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index ce7ad71..73c649e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 18c44f5..450332a 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v10 3/5] eal: support link bonding device initialization 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (7 preceding siblings ...) 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 2/5] ethdev: add unique name to devices Declan Doherty @ 2014-06-27 10:18 ` Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 4/5] bond: unit tests Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 5/5] bond: testpmd support Declan Doherty 10 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-27 10:18 UTC (permalink / raw) To: dev Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- lib/librte_eal/bsdapp/eal/eal.c | 10 ++++- lib/librte_eal/common/eal_common_dev.c | 58 ++++++++++++++++++-------- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 --- lib/librte_eal/common/include/rte_dev.h | 14 ++++++- lib/librte_eal/linuxapp/eal/eal.c | 11 +++++- 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index a1f014f..c53f63e 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -874,7 +874,7 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -906,6 +906,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..1194419 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) } int -rte_eal_dev_init(void) +rte_eal_dev_init(uint8_t init_pri) { struct rte_devargs *devargs; struct rte_driver *driver; @@ -80,30 +80,52 @@ rte_eal_dev_init(void) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) - continue; + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, + * virtual devices are initialized pre PCI probing and bonded + * device are post pci probing */ + if ((driver->type == PMD_VDEV && init_pri == + PMD_INIT_PRE_PCI_PROBE) || + (driver->type == PMD_BDEV && init_pri == + PMD_INIT_POST_PCI_PROBE)) { - /* search a driver prefix in virtual device name */ - if (!strncmp(driver->name, devargs->virtual.drv_name, - strlen(driver->name))) { - driver->init(devargs->virtual.drv_name, - devargs->args); - break; + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + printf("init (%u) %s\n", init_pri, devargs->virtual.drv_name); + driver->init(devargs->virtual.drv_name, + devargs->args); + break; + } } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + /* If initializing pre PCI probe, then we don't expect a bonded driver + * to be found */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE && + strncmp(PMD_BOND_NAME, devargs->virtual.drv_name, + strlen(PMD_BOND_NAME)) != 0) { + if (driver == NULL) { + rte_panic("no driver found for virtual device %s\n", + devargs->virtual.drv_name); + } + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && + strncmp(PMD_BOND_NAME, devargs->virtual.drv_name, + strlen(PMD_BOND_NAME)) == 0) { + if (driver == NULL) { + rte_panic("no driver found for bonded device %s\n", + devargs->virtual.drv_name); + } } } - /* Once the vdevs are initalized, start calling all the pdev drivers */ - TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_PDEV) - continue; - /* PDEV drivers don't get passed any parameters */ - driver->init(NULL, NULL); + /* Once the vdevs are initialized, start calling all the pdev drivers */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_PDEV) + continue; + /* PDEV drivers don't get passed any parameters */ + driver->init(NULL, NULL); + } } return 0; } diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index af809a8..c637361 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -150,6 +150,9 @@ rte_eal_pci_probe(void) probe_all = 1; TAILQ_FOREACH(dev, &pci_device_list, next) { + /* check if device has already been initialized */ + if (dev->driver != NULL) + continue; /* set devargs in PCI structure */ devargs = pci_devargs_lookup(dev); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..b440ffb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -196,11 +196,4 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); -/** - * This function initialises any virtual devices - * - * This function is private to the EAL. - */ -int rte_eal_dev_init(void); - #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..d89f1a5 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,16 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ +}; + +#define PMD_BOND_NAME "eth_bond" + +/** + * Driver initialization */ +enum pmd_init_priority { + PMD_INIT_PRE_PCI_PROBE = 0, + PMD_INIT_POST_PCI_PROBE = 1, }; /** @@ -93,9 +103,9 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** - * Initalize all the registered drivers in this process + * Initialize all the registered drivers in this process */ -int rte_eal_dev_init(void); +int rte_eal_dev_init(uint8_t init_priority); #define PMD_REGISTER_DRIVER(d)\ void devinitfn_ ##d(void);\ diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index d204387..573fd06 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -75,6 +75,7 @@ #include <rte_atomic.h> #include <malloc_heap.h> #include <rte_eth_ring.h> +#include <rte_dev.h> #include "eal_private.h" #include "eal_thread.h" @@ -1097,7 +1098,7 @@ rte_eal_init(int argc, char **argv) RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", rte_config.master_lcore, (int)thread_id); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -1127,6 +1128,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v10 4/5] bond: unit tests 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (8 preceding siblings ...) 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 3/5] eal: support link bonding device initialization Declan Doherty @ 2014-06-27 10:18 ` Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 5/5] bond: testpmd support Declan Doherty 10 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-27 10:18 UTC (permalink / raw) To: dev Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4982 insertions(+), 1 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 45d0cf2..cec2f2f 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -102,7 +102,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index c9dc085..5f23420 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -159,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -227,6 +231,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..5d539f1 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,287 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index c54e7ef..c00e1d6 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -97,6 +97,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..3f14278 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v10 5/5] bond: testpmd support 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon ` (9 preceding siblings ...) 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 4/5] bond: unit tests Declan Doherty @ 2014-06-27 10:18 ` Declan Doherty 10 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-27 10:18 UTC (permalink / raw) To: dev - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> Acked-by: Pablo de Lara <pablo.de.lara.guarch@intel.com> --- app/test-pmd/cmdline.c | 579 +++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 619 insertions(+), 9 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index f61a31c..345be11 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -412,6 +415,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -3039,6 +3067,547 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static int bond_dev_num = 0; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int port_id; + + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "eth_bond_testpmd_%d", + bond_dev_num++); + + /* Create a new bonded device. */ + port_id = rte_eth_bond_create(ethdev_name, res->mode, res->socket); + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("Created new bonded device %s on (port %d).\n", ethdev_name, + port_id); + + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -6768,6 +7337,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 32f2134..c72f6ee 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -252,6 +252,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -265,7 +266,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 4bca0b0..9573a43 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 16fe596..e8a4b45 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1252,7 +1278,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1380,10 +1406,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; @@ -1826,9 +1853,6 @@ main(int argc, char** argv) if (diag < 0) rte_panic("Cannot init EAL\n"); - if (rte_eal_pci_probe()) - rte_panic("Cannot probe PCI\n"); - nb_ports = (portid_t) rte_eth_dev_count(); if (nb_ports == 0) rte_exit(EXIT_FAILURE, "No probed ethernet devices - " diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index e263616..ac86bfe 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -281,6 +281,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -454,6 +455,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); void port_mtu_set(portid_t port_id, uint16_t mtu); -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v8 1/6] Link Bonding Library (lib/librte_pmd_bond) 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 " Declan Doherty @ 2014-06-25 20:07 ` Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 2/6] Support for unique interface naming of pmds Declan Doherty ` (4 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-25 20:07 UTC (permalink / raw) To: dev Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 34 + lib/librte_pmd_bond/rte_eth_bond.h | 255 ++++++ lib/librte_pmd_bond/rte_eth_bond_api.c | 670 +++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_pmd.c | 1228 ++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond_private.h | 218 +++++ lib/librte_pmd_bond/rte_eth_bond_vargs.c | 255 ++++++ mk/rte.app.mk | 5 + 10 files changed, 2676 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_api.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_pmd.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond_private.h create mode 100644 lib/librte_pmd_bond/rte_eth_bond_vargs.c diff --git a/config/common_bsdapp b/config/common_bsdapp index 989e1da..214398b 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -206,6 +206,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 5b896c3..2bf90df 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -244,6 +244,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/lib/Makefile b/lib/Makefile index c58c0c9..88e875f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -49,6 +49,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..4128f61 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,34 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_api.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_pmd.c +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond_vargs.c + + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..7cf9dd8 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/librte_pmd_bond/rte_eth_bond_api.c b/lib/librte_pmd_bond/rte_eth_bond_api.c new file mode 100644 index 0000000..cc0bd98 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_api.c @@ -0,0 +1,670 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + +uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + +const char *driver_name = "Link Bonding PMD"; + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket\n"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket\n"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket\n"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket\n"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket\n"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev\n"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_pmd_bond/rte_eth_bond_pmd.c b/lib/librte_pmd_bond/rte_eth_bond_pmd.c new file mode 100644 index 0000000..d41e1d9 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_pmd.c @@ -0,0 +1,1228 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + + +/* Link Bonding KVARG option definitions for --vdev */ +#define PMD_BOND_KVARG RTE_PMD_BOND + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int slave_idx = 0; + int i, cs_idx = 0; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + cs_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cs_idx][(slave_nb_pkts[cs_idx])++] = bufs[i]; + } + + /* increment current slave index so the next call to tx burst starts on the + * next slave */ + slave_idx = ++cs_idx; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) + if (slave_nb_pkts[i] > 0) + num_tx_total += rte_eth_tx_burst(slaves[i], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + internals->mode = mode; + + return 0; +} + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + + + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static int +rte_pmd_bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver rte_pmd_bond_drv = { + .name = PMD_BOND_KVARG, + .type = PMD_BDEV, + .init = rte_pmd_bond_init, +}; + +PMD_REGISTER_DRIVER(rte_pmd_bond_drv); + diff --git a/lib/librte_pmd_bond/rte_eth_bond_private.h b/lib/librte_pmd_bond/rte_eth_bond_private.h new file mode 100644 index 0000000..c646f24 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_private.h @@ -0,0 +1,218 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_PRIVATE_H_ +#define _RTE_ETH_BOND_PRIVATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ethdev.h> + +#include "rte_eth_bond.h" + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +extern const char *pmd_bond_init_valid_arguments[]; + +extern const char *driver_name; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +extern struct eth_dev_ops default_dev_ops; + +int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev); + +int +valid_port_id(uint8_t port_id); + +int +valid_bonded_port_id(uint8_t port_id); + +int +valid_slave_port_id(uint8_t port_id); + +void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link); +void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev); + +int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link); + +int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr); + +int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev); + +uint8_t +number_of_sockets(void); + +int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode); + +int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev); + +struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id); + + +void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param); + + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/librte_pmd_bond/rte_eth_bond_vargs.c b/lib/librte_pmd_bond/rte_eth_bond_vargs.c new file mode 100644 index 0000000..3cc2681 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond_vargs.c @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <rte_devargs.h> +#include <rte_kvargs.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" +#include "rte_eth_bond_private.h" + + +const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + return port_id; +} + +int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 8db9cde..2539f46 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -205,8 +205,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v8 2/6] Support for unique interface naming of pmds 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 " Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty @ 2014-06-25 20:07 ` Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 3/6] EAL support for link bonding device initialization Declan Doherty ` (3 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-25 20:07 UTC (permalink / raw) To: dev Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_ether/rte_ethdev.c | 32 +++++++++++++++++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 +++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 ++++++++++---------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++-------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 7256841..d938603 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -65,6 +65,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -153,21 +154,40 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static struct rte_eth_dev * +rte_eth_dev_allocated(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return &rte_eth_devices[i]; + } + return NULL; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; if (nb_ports == RTE_MAX_ETHPORTS) { - PMD_DEBUG_TRACE("Reached maximum number of ethernet ports\n"); + PMD_DEBUG_TRACE("Reached maximum number of Ethernet ports\n"); return NULL; } if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_allocated(name) != NULL) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + rte_snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), + "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } @@ -178,11 +198,17 @@ rte_eth_dev_init(struct rte_pci_driver *pci_drv, { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique Ethernet device name using PCI address */ + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 2406e45..50df654 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1497,6 +1497,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1505,6 +1507,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1560,10 +1564,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index 10d4e24..f9174f1 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - rte_snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + rte_snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - rte_snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + rte_snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 7c4d3fe..a0b4bdd 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v8 3/6] EAL support for link bonding device initialization 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty ` (2 preceding siblings ...) 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 2/6] Support for unique interface naming of pmds Declan Doherty @ 2014-06-25 20:07 ` Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 4/6] Link bonding Unit Tests Declan Doherty ` (2 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-25 20:07 UTC (permalink / raw) To: dev Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_eal/bsdapp/eal/eal.c | 10 ++++- lib/librte_eal/common/eal_common_dev.c | 58 ++++++++++++++++++-------- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 --- lib/librte_eal/common/include/rte_dev.h | 13 +++++- lib/librte_eal/linuxapp/eal/eal.c | 11 +++++- 6 files changed, 73 insertions(+), 29 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index a1f014f..c53f63e 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -874,7 +874,7 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -906,6 +906,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..8e80093 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) } int -rte_eal_dev_init(void) +rte_eal_dev_init(uint8_t init_pri) { struct rte_devargs *devargs; struct rte_driver *driver; @@ -80,30 +80,52 @@ rte_eal_dev_init(void) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) - continue; + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, + * virtual devices are initialized pre PCI probing and bonded + * device are post pci probing */ + if ((driver->type == PMD_VDEV && init_pri == + PMD_INIT_PRE_PCI_PROBE) || + (driver->type == PMD_BDEV && init_pri == + PMD_INIT_POST_PCI_PROBE)) { - /* search a driver prefix in virtual device name */ - if (!strncmp(driver->name, devargs->virtual.drv_name, - strlen(driver->name))) { - driver->init(devargs->virtual.drv_name, - devargs->args); - break; + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + printf("init (%u) %s\n", init_pri, devargs->virtual.drv_name); + driver->init(devargs->virtual.drv_name, + devargs->args); + break; + } } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + /* If initializing pre PCI probe, then we don't expect a bonded driver + * to be found */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) != 0) { + if (driver == NULL) { + rte_panic("no driver found for virtual device %s\n", + devargs->virtual.drv_name); + } + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) == 0) { + if (driver == NULL) { + rte_panic("no driver found for bonded device %s\n", + devargs->virtual.drv_name); + } } } - /* Once the vdevs are initalized, start calling all the pdev drivers */ - TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_PDEV) - continue; - /* PDEV drivers don't get passed any parameters */ - driver->init(NULL, NULL); + /* Once the vdevs are initialized, start calling all the pdev drivers */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_PDEV) + continue; + /* PDEV drivers don't get passed any parameters */ + driver->init(NULL, NULL); + } } return 0; } diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index af809a8..c637361 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -150,6 +150,9 @@ rte_eal_pci_probe(void) probe_all = 1; TAILQ_FOREACH(dev, &pci_device_list, next) { + /* check if device has already been initialized */ + if (dev->driver != NULL) + continue; /* set devargs in PCI structure */ devargs = pci_devargs_lookup(dev); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..b440ffb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -196,11 +196,4 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); -/** - * This function initialises any virtual devices - * - * This function is private to the EAL. - */ -int rte_eal_dev_init(void); - #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..eaf3284 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,15 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ +}; + +#define RTE_PMD_BOND ("eth_bond") +/** + * Driver initialization */ +enum pmd_init_priority { + PMD_INIT_PRE_PCI_PROBE = 0, + PMD_INIT_POST_PCI_PROBE = 1, }; /** @@ -93,9 +102,9 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** - * Initalize all the registered drivers in this process + * Initialize all the registered drivers in this process */ -int rte_eal_dev_init(void); +int rte_eal_dev_init(uint8_t init_priority); #define PMD_REGISTER_DRIVER(d)\ void devinitfn_ ##d(void);\ diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index d204387..573fd06 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -75,6 +75,7 @@ #include <rte_atomic.h> #include <malloc_heap.h> #include <rte_eth_ring.h> +#include <rte_dev.h> #include "eal_private.h" #include "eal_thread.h" @@ -1097,7 +1098,7 @@ rte_eal_init(int argc, char **argv) RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", rte_config.master_lcore, (int)thread_id); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -1127,6 +1128,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v8 4/6] Link bonding Unit Tests 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty ` (3 preceding siblings ...) 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 3/6] EAL support for link bonding device initialization Declan Doherty @ 2014-06-25 20:07 ` Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 5/6] testpmd link bonding additions Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 6/6] Link Bonding Library doxygen additions Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-25 20:07 UTC (permalink / raw) To: dev Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4982 insertions(+), 1 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 9c52460..643f1b9 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -102,7 +102,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index c9dc085..5f23420 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -159,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -227,6 +231,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..5d539f1 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,287 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index c54e7ef..c00e1d6 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -97,6 +97,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..6662059 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v8 5/6] testpmd link bonding additions 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty ` (4 preceding siblings ...) 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 4/6] Link bonding Unit Tests Declan Doherty @ 2014-06-25 20:07 ` Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 6/6] Link Bonding Library doxygen additions Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-25 20:07 UTC (permalink / raw) To: dev - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 579 +++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 619 insertions(+), 9 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 3298360..fcc6449 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -404,6 +407,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -3031,6 +3059,547 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static int bond_dev_num = 0; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int port_id; + + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "eth_bond_testpmd_%d", + bond_dev_num++); + + /* Create a new bonded device. */ + port_id = rte_eth_bond_create(ethdev_name, res->mode, res->socket); + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("Created new bonded device %s on (port %d).\n", ethdev_name, + port_id); + + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -6572,6 +7141,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 255f82d..38a93a1 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -252,6 +252,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -265,7 +266,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 3ff4f81..69b47e8 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 546d429..1e55815 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1252,7 +1278,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1380,10 +1406,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; @@ -1826,9 +1853,6 @@ main(int argc, char** argv) if (diag < 0) rte_panic("Cannot init EAL\n"); - if (rte_eal_pci_probe()) - rte_panic("Cannot probe PCI\n"); - nb_ports = (portid_t) rte_eth_dev_count(); if (nb_ports == 0) rte_exit(EXIT_FAILURE, "No probed ethernet devices - " diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 5839f93..b8288c1 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); void port_mtu_set(portid_t port_id, uint16_t mtu); -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v8 6/6] Link Bonding Library doxygen additions 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty ` (5 preceding siblings ...) 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 5/6] testpmd link bonding additions Declan Doherty @ 2014-06-25 20:07 ` Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-25 20:07 UTC (permalink / raw) To: dev Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 7b26e98..ee3ad4f 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_eth_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index f380d9a..b15a340 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_pmd_bond \ lib/librte_eal/common/include \ lib/librte_acl \ lib/librte_distributor \ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v7 1/6] Link Bonding Library (lib/librte_pmd_bond) 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty @ 2014-06-24 16:03 ` Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 2/6] Support for unique interface naming of pmds Declan Doherty ` (4 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 16:03 UTC (permalink / raw) To: dev Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2142 ++++++++++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 +++++ mk/rte.app.mk | 5 + 7 files changed, 2445 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 989e1da..214398b 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -206,6 +206,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 5b896c3..2bf90df 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -244,6 +244,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/lib/Makefile b/lib/Makefile index c58c0c9..88e875f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -49,6 +49,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..51f6159 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,32 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.c b/lib/librte_pmd_bond/rte_eth_bond.c new file mode 100644 index 0000000..1b0cf81 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.c @@ -0,0 +1,2142 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" + +/* Link Bonding KVARG option defintions for --vdev */ +#define PMD_BOND_KVARG RTE_PMD_BOND + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +static const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int slave_idx = 0; + int i, cs_idx = 0; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + cs_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cs_idx][(slave_nb_pkts[cs_idx])++] = bufs[i]; + } + + /* increment current slave index so the next call to tx burst starts on the + * next slave */ + slave_idx = ++cs_idx; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) + if (slave_nb_pkts[i] > 0) + num_tx_total += rte_eth_tx_burst(slaves[i], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + + + +static void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + + return port_id; +} +static int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +static int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +static int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +static int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +static int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +static int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} + + +static int +rte_pmd_bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver rte_pmd_bond_drv = { + .name = PMD_BOND_KVARG, + .type = PMD_BDEV, + .init = rte_pmd_bond_init, +}; + +PMD_REGISTER_DRIVER(rte_pmd_bond_drv); + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..7cf9dd8 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 8db9cde..2539f46 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -205,8 +205,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v7 2/6] Support for unique interface naming of pmds 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty @ 2014-06-24 16:03 ` Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization Declan Doherty ` (3 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 16:03 UTC (permalink / raw) To: dev Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_ether/rte_ethdev.c | 33 +++++++++++++++++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 +++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 ++++++++++---------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 67 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 7256841..2225c0a 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -65,6 +65,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -153,21 +154,41 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static struct rte_eth_dev * +rte_eth_dev_allocated(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return &rte_eth_devices[i]; + } + return NULL; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; if (nb_ports == RTE_MAX_ETHPORTS) { - PMD_DEBUG_TRACE("Reached maximum number of ethernet ports\n"); + PMD_DEBUG_TRACE("Reached maximum number of Ethernet ports\n"); return NULL; } if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_allocated(name) != NULL) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n", + name); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + rte_snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), + "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } @@ -178,11 +199,17 @@ rte_eth_dev_init(struct rte_pci_driver *pci_drv, { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique Ethernet device name using PCI address */ + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 2406e45..50df654 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1497,6 +1497,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1505,6 +1507,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1560,10 +1564,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index 10d4e24..f9174f1 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - rte_snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + rte_snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - rte_snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + rte_snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 7c4d3fe..a0b4bdd 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty ` (2 preceding siblings ...) 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 2/6] Support for unique interface naming of pmds Declan Doherty @ 2014-06-24 16:03 ` Declan Doherty 2014-06-25 13:54 ` Thomas Monjalon 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 4/6] Link bonding Unit Tests Declan Doherty ` (2 subsequent siblings) 6 siblings, 1 reply; 127+ messages in thread From: Declan Doherty @ 2014-06-24 16:03 UTC (permalink / raw) To: dev Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_eal/bsdapp/eal/eal.c | 10 ++++- lib/librte_eal/common/eal_common_dev.c | 58 ++++++++++++++++++-------- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 --- lib/librte_eal/common/include/rte_dev.h | 13 +++++- lib/librte_eal/linuxapp/eal/eal.c | 11 +++++- 6 files changed, 73 insertions(+), 29 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index a1f014f..c53f63e 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -874,7 +874,7 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -906,6 +906,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..8e80093 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) } int -rte_eal_dev_init(void) +rte_eal_dev_init(uint8_t init_pri) { struct rte_devargs *devargs; struct rte_driver *driver; @@ -80,30 +80,52 @@ rte_eal_dev_init(void) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) - continue; + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, + * virtual devices are initialized pre PCI probing and bonded + * device are post pci probing */ + if ((driver->type == PMD_VDEV && init_pri == + PMD_INIT_PRE_PCI_PROBE) || + (driver->type == PMD_BDEV && init_pri == + PMD_INIT_POST_PCI_PROBE)) { - /* search a driver prefix in virtual device name */ - if (!strncmp(driver->name, devargs->virtual.drv_name, - strlen(driver->name))) { - driver->init(devargs->virtual.drv_name, - devargs->args); - break; + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + printf("init (%u) %s\n", init_pri, devargs->virtual.drv_name); + driver->init(devargs->virtual.drv_name, + devargs->args); + break; + } } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + /* If initializing pre PCI probe, then we don't expect a bonded driver + * to be found */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) != 0) { + if (driver == NULL) { + rte_panic("no driver found for virtual device %s\n", + devargs->virtual.drv_name); + } + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) == 0) { + if (driver == NULL) { + rte_panic("no driver found for bonded device %s\n", + devargs->virtual.drv_name); + } } } - /* Once the vdevs are initalized, start calling all the pdev drivers */ - TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_PDEV) - continue; - /* PDEV drivers don't get passed any parameters */ - driver->init(NULL, NULL); + /* Once the vdevs are initialized, start calling all the pdev drivers */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_PDEV) + continue; + /* PDEV drivers don't get passed any parameters */ + driver->init(NULL, NULL); + } } return 0; } diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index af809a8..c637361 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -150,6 +150,9 @@ rte_eal_pci_probe(void) probe_all = 1; TAILQ_FOREACH(dev, &pci_device_list, next) { + /* check if device has already been initialized */ + if (dev->driver != NULL) + continue; /* set devargs in PCI structure */ devargs = pci_devargs_lookup(dev); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..b440ffb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -196,11 +196,4 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); -/** - * This function initialises any virtual devices - * - * This function is private to the EAL. - */ -int rte_eal_dev_init(void); - #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..eaf3284 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,15 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ +}; + +#define RTE_PMD_BOND ("eth_bond") +/** + * Driver initialization */ +enum pmd_init_priority { + PMD_INIT_PRE_PCI_PROBE = 0, + PMD_INIT_POST_PCI_PROBE = 1, }; /** @@ -93,9 +102,9 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** - * Initalize all the registered drivers in this process + * Initialize all the registered drivers in this process */ -int rte_eal_dev_init(void); +int rte_eal_dev_init(uint8_t init_priority); #define PMD_REGISTER_DRIVER(d)\ void devinitfn_ ##d(void);\ diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index d204387..573fd06 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -75,6 +75,7 @@ #include <rte_atomic.h> #include <malloc_heap.h> #include <rte_eth_ring.h> +#include <rte_dev.h> #include "eal_private.h" #include "eal_thread.h" @@ -1097,7 +1098,7 @@ rte_eal_init(int argc, char **argv) RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", rte_config.master_lcore, (int)thread_id); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -1127,6 +1128,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization Declan Doherty @ 2014-06-25 13:54 ` Thomas Monjalon 2014-06-25 14:41 ` Doherty, Declan 0 siblings, 1 reply; 127+ messages in thread From: Thomas Monjalon @ 2014-06-25 13:54 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, 2014-06-24 17:03, Declan Doherty: > Updating functionality in EAL to support adding link bonding > devices via –vdev option. Link bonding devices will be > initialized after all physical devices have been probed and > initialized. [...] > --- a/lib/librte_eal/common/eal_common_dev.c > +++ b/lib/librte_eal/common/eal_common_dev.c > @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) > } > > int > -rte_eal_dev_init(void) > +rte_eal_dev_init(uint8_t init_pri) > { > struct rte_devargs *devargs; > struct rte_driver *driver; > @@ -80,30 +80,52 @@ rte_eal_dev_init(void) > continue; > > TAILQ_FOREACH(driver, &dev_driver_list, next) { > - if (driver->type != PMD_VDEV) > - continue; > + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, > + * virtual devices are initialized pre PCI probing and bonded > + * device are post pci probing */ > + if ((driver->type == PMD_VDEV && init_pri == > + PMD_INIT_PRE_PCI_PROBE) || > + (driver->type == PMD_BDEV && init_pri == > + PMD_INIT_POST_PCI_PROBE)) { > > - /* search a driver prefix in virtual device name */ > - if (!strncmp(driver->name, devargs->virtual.drv_name, > - strlen(driver->name))) { > - driver->init(devargs->virtual.drv_name, > - devargs->args); > - break; > + /* search a driver prefix in virtual device name */ > + if (!strncmp(driver->name, devargs->virtual.drv_name, > + strlen(driver->name))) { > + printf("init (%u) %s\n", init_pri, devargs- >virtual.drv_name); > + driver->init(devargs->virtual.drv_name, > + devargs->args); > + break; > + } > } > } > > - if (driver == NULL) { > - rte_panic("no driver found for %s\n", > - devargs->virtual.drv_name); > + /* If initializing pre PCI probe, then we don't expect a bonded driver > + * to be found */ > + if (init_pri == PMD_INIT_PRE_PCI_PROBE && > + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, > + strlen(RTE_PMD_BOND)) != 0) { > + if (driver == NULL) { > + rte_panic("no driver found for virtual device %s\n", > + devargs->virtual.drv_name); > + } > + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && > + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, > + strlen(RTE_PMD_BOND)) == 0) { > + if (driver == NULL) { > + rte_panic("no driver found for bonded device %s\n", > + devargs->virtual.drv_name); > + } > } > } > > - /* Once the vdevs are initalized, start calling all the pdev drivers */ > - TAILQ_FOREACH(driver, &dev_driver_list, next) { > - if (driver->type != PMD_PDEV) > - continue; > - /* PDEV drivers don't get passed any parameters */ > - driver->init(NULL, NULL); > + /* Once the vdevs are initialized, start calling all the pdev drivers */ > + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { > + TAILQ_FOREACH(driver, &dev_driver_list, next) { > + if (driver->type != PMD_PDEV) > + continue; > + /* PDEV drivers don't get passed any parameters */ > + driver->init(NULL, NULL); > + } > } > return 0; > } [...] > --- a/lib/librte_eal/linuxapp/eal/eal.c > +++ b/lib/librte_eal/linuxapp/eal/eal.c > @@ -75,6 +75,7 @@ > #include <rte_atomic.h> > #include <malloc_heap.h> > #include <rte_eth_ring.h> > +#include <rte_dev.h> > > #include "eal_private.h" > #include "eal_thread.h" > @@ -1097,7 +1098,7 @@ rte_eal_init(int argc, char **argv) > RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", > rte_config.master_lcore, (int)thread_id); > > - if (rte_eal_dev_init() < 0) > + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) > rte_panic("Cannot init pmd devices\n"); > > RTE_LCORE_FOREACH_SLAVE(i) { > @@ -1127,6 +1128,14 @@ rte_eal_init(int argc, char **argv) > rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); > rte_eal_mp_wait_lcore(); > > + /* Probe & Initialize PCI devices */ > + if (rte_eal_pci_probe()) > + rte_panic("Cannot probe PCI\n"); > + > + /* Initialize any outstanding devices */ > + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) > + rte_panic("Cannot init pmd devices\n"); > + > return fctret; > } Not sure to understand why you need to split rte_eal_dev_init() in 2 steps. Should it be possible to keep existing rte_eal_dev_init() behaviour and makes further initialization when calling rte_eth_dev_configure()? I've seen it's empty for bonding device: static int bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) { return 0; } Thanks -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization 2014-06-25 13:54 ` Thomas Monjalon @ 2014-06-25 14:41 ` Doherty, Declan 2014-06-25 16:00 ` Thomas Monjalon 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-25 14:41 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev > -----Original Message----- > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > Sent: Wednesday, June 25, 2014 2:55 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [PATCH v7 3/6] EAL support for link bonding device initialization > > Hi Declan, > > 2014-06-24 17:03, Declan Doherty: > > Updating functionality in EAL to support adding link bonding > > devices via –vdev option. Link bonding devices will be > > initialized after all physical devices have been probed and > > initialized. > [...] > > > Not sure to understand why you need to split rte_eal_dev_init() in 2 steps. > Should it be possible to keep existing rte_eal_dev_init() behaviour and makes > further initialization when calling rte_eth_dev_configure()? > I've seen it's empty for bonding device: > > Thanks > -- > Thomas Hi Thomas, that need to split rte_eal_dev_init into 2 steps doesn't come explicitly from the bonded device itself, as a bonded device could be created at any time during initialization, the issue arises from the fact that none of physical devices are allocated/initialized until after pci_probe_all_drivers() is called, this puts an explicit constraint on when it is possible to create a bonded device which has physical devices as slaves, as the phyiscal devices don't exist at the initial call to rte_eal_dev_init() and therefore can't be added as slaves to the bonded device. It isn't possible to keep the rte_eal_dev_init() behavior and use rte_eth_dev_configure() to complete initialization without radically changing the behavior of the bonding library, and the current functionality of rte_eth_bond_slave_add(), as this would need to no longer actually add a slave, but to save the name of a slave to be retrieved at some point in the future to be added as a slave to the bonded device. Declan ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization 2014-06-25 14:41 ` Doherty, Declan @ 2014-06-25 16:00 ` Thomas Monjalon 2014-06-25 16:15 ` Richardson, Bruce 0 siblings, 1 reply; 127+ messages in thread From: Thomas Monjalon @ 2014-06-25 16:00 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev 2014-06-25 14:41, Doherty, Declan: > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > > Not sure to understand why you need to split rte_eal_dev_init() in 2 > > steps. > > Should it be possible to keep existing rte_eal_dev_init() > > behaviour and makes further initialization when calling > > rte_eth_dev_configure()? > > I've seen it's empty for bonding device: > > Hi Thomas, that need to split rte_eal_dev_init into 2 steps doesn't come > explicitly from the bonded device itself, as a bonded device could be > created at any time during initialization, the issue arises from the fact > that none of physical devices are allocated/initialized until after > pci_probe_all_drivers() is called, this puts an explicit constraint on when > it is possible to create a bonded device which has physical devices as > slaves, as the phyiscal devices don't exist at the initial call to > rte_eal_dev_init() and therefore can't be added as slaves to the bonded > device. > It isn't possible to keep the rte_eal_dev_init() behavior and use > rte_eth_dev_configure() to complete initialization without radically > changing the behavior of the bonding library, and the current > functionality of rte_eth_bond_slave_add(), as this would need to no longer > actually add a slave, but to save the name of a slave to be retrieved at > some point in the future to be added as a slave to the bonded device. I'm sure it would be much cleaner if you split rte_eth_bond_slave_add() instead of splitting rte_eal_dev_init(). If I understand well, you should first save kvargs for slaves and add them at configure time. Then you could remove PMD_BDEV type and use PMD_VDEV. I know it's an additional work but API cleaning really deserves it. Thanks -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization 2014-06-25 16:00 ` Thomas Monjalon @ 2014-06-25 16:15 ` Richardson, Bruce 0 siblings, 0 replies; 127+ messages in thread From: Richardson, Bruce @ 2014-06-25 16:15 UTC (permalink / raw) To: Thomas Monjalon, Doherty, Declan; +Cc: dev > -----Original Message----- > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Thomas Monjalon > Sent: Wednesday, June 25, 2014 9:01 AM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device > initialization > > 2014-06-25 14:41, Doherty, Declan: > > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > > > Not sure to understand why you need to split rte_eal_dev_init() in 2 > > > steps. > > > Should it be possible to keep existing rte_eal_dev_init() > > > behaviour and makes further initialization when calling > > > rte_eth_dev_configure()? > > > I've seen it's empty for bonding device: > > > > Hi Thomas, that need to split rte_eal_dev_init into 2 steps doesn't come > > explicitly from the bonded device itself, as a bonded device could be > > created at any time during initialization, the issue arises from the fact > > that none of physical devices are allocated/initialized until after > > pci_probe_all_drivers() is called, this puts an explicit constraint on when > > it is possible to create a bonded device which has physical devices as > > slaves, as the phyiscal devices don't exist at the initial call to > > rte_eal_dev_init() and therefore can't be added as slaves to the bonded > > device. > > It isn't possible to keep the rte_eal_dev_init() behavior and use > > rte_eth_dev_configure() to complete initialization without radically > > changing the behavior of the bonding library, and the current > > functionality of rte_eth_bond_slave_add(), as this would need to no longer > > actually add a slave, but to save the name of a slave to be retrieved at > > some point in the future to be added as a slave to the bonded device. > > I'm sure it would be much cleaner if you split rte_eth_bond_slave_add() > instead of splitting rte_eal_dev_init(). > If I understand well, you should first save kvargs for slaves and add them at > configure time. > > Then you could remove PMD_BDEV type and use PMD_VDEV. > > I know it's an additional work but API cleaning really deserves it. > Or else we could just go back to the earlier versions of the submission and accept the fact that some types of devices are designed to be used from code via an API and that we don't need to be able to set up every type of device via the application command-line. The code for this feature and rework to EAL has just got continually more complicated every time due to the need to continually workaround gotchas when trying to enable this for second-level ethdevs. /Bruce ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v7 4/6] Link bonding Unit Tests 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty ` (3 preceding siblings ...) 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization Declan Doherty @ 2014-06-24 16:03 ` Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 5/6] testpmd link bonding additions Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 16:03 UTC (permalink / raw) To: dev Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4982 insertions(+), 1 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 9c52460..643f1b9 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -102,7 +102,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index c9dc085..5f23420 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -159,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -227,6 +231,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..5d539f1 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,287 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index c54e7ef..c00e1d6 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -97,6 +97,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..6662059 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v7 5/6] testpmd link bonding additions 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty ` (4 preceding siblings ...) 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 4/6] Link bonding Unit Tests Declan Doherty @ 2014-06-24 16:03 ` Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 16:03 UTC (permalink / raw) To: dev - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 579 +++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 619 insertions(+), 9 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 3298360..fcc6449 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -404,6 +407,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -3031,6 +3059,547 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static int bond_dev_num = 0; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int port_id; + + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "eth_bond_testpmd_%d", + bond_dev_num++); + + /* Create a new bonded device. */ + port_id = rte_eth_bond_create(ethdev_name, res->mode, res->socket); + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("Created new bonded device %s on (port %d).\n", ethdev_name, + port_id); + + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -6572,6 +7141,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 255f82d..38a93a1 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -252,6 +252,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -265,7 +266,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 3ff4f81..69b47e8 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 546d429..1e55815 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1252,7 +1278,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1380,10 +1406,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; @@ -1826,9 +1853,6 @@ main(int argc, char** argv) if (diag < 0) rte_panic("Cannot init EAL\n"); - if (rte_eal_pci_probe()) - rte_panic("Cannot probe PCI\n"); - nb_ports = (portid_t) rte_eth_dev_count(); if (nb_ports == 0) rte_exit(EXIT_FAILURE, "No probed ethernet devices - " diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 5839f93..b8288c1 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); void port_mtu_set(portid_t port_id, uint16_t mtu); -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty ` (5 preceding siblings ...) 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 5/6] testpmd link bonding additions Declan Doherty @ 2014-06-24 16:03 ` Declan Doherty 2014-06-25 13:43 ` Thomas Monjalon 6 siblings, 1 reply; 127+ messages in thread From: Declan Doherty @ 2014-06-24 16:03 UTC (permalink / raw) To: dev Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 7b26e98..ee3ad4f 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_eth_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index f380d9a..b15a340 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_pmd_bond \ lib/librte_eal/common/include \ lib/librte_acl \ lib/librte_distributor \ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions Declan Doherty @ 2014-06-25 13:43 ` Thomas Monjalon 2014-06-25 14:19 ` Doherty, Declan 0 siblings, 1 reply; 127+ messages in thread From: Thomas Monjalon @ 2014-06-25 13:43 UTC (permalink / raw) To: Declan Doherty; +Cc: dev Hi Declan, Since bonding library is a PMD, there should have no need of integrate it in the API documentation. I hadn't time to review it yet, so I would miss something. Do you mean it can be used directly by the application with a specific API? In this case, you could explain it in the commit log. 2014-06-24 17:03, Declan Doherty: > --- a/doc/doxy-api-index.md > +++ b/doc/doxy-api-index.md > @@ -36,6 +36,7 @@ API {#index} > There are many libraries, so their headers may be grouped by topics: > > - **device**: > + [bond] (@ref rte_eth_bond.h), > [ethdev] (@ref rte_ethdev.h), > [devargs] (@ref rte_devargs.h), > [KNI] (@ref rte_kni.h), ethdev should be first in this list. > --- a/doc/doxy-api.conf > +++ b/doc/doxy-api.conf > @@ -30,6 +30,7 @@ > > PROJECT_NAME = DPDK > INPUT = doc/doxy-api-index.md \ > + lib/librte_pmd_bond \ > lib/librte_eal/common/include \ > lib/librte_acl \ > lib/librte_distributor \ Libraries should be in alphabetical order here. Thanks -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions 2014-06-25 13:43 ` Thomas Monjalon @ 2014-06-25 14:19 ` Doherty, Declan 2014-06-25 14:23 ` Thomas Monjalon 0 siblings, 1 reply; 127+ messages in thread From: Doherty, Declan @ 2014-06-25 14:19 UTC (permalink / raw) To: Thomas Monjalon; +Cc: dev > -----Original Message----- > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > Sent: Wednesday, June 25, 2014 2:44 PM > To: Doherty, Declan > Cc: dev@dpdk.org > Subject: Re: [PATCH v7 6/6] Link Bonding Library doxygen additions > > Hi Declan, > > Since bonding library is a PMD, there should have no need of integrate it > in the API documentation. > I hadn't time to review it yet, so I would miss something. > Do you mean it can be used directly by the application with a specific API? > In this case, you could explain it in the commit log. > Hi Thomas, the bonding library has a API which supports creation and management of bonded devices directly from within applications, but more importantly provides the APIs required to dynamically add and remove slaves from a bonded device, this functionality will be required to support live migration of VMs using bonded ports in a future releases. ^ permalink raw reply [flat|nested] 127+ messages in thread
* Re: [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions 2014-06-25 14:19 ` Doherty, Declan @ 2014-06-25 14:23 ` Thomas Monjalon 0 siblings, 0 replies; 127+ messages in thread From: Thomas Monjalon @ 2014-06-25 14:23 UTC (permalink / raw) To: Doherty, Declan; +Cc: dev 2014-06-25 14:19, Doherty, Declan: > > -----Original Message----- > > From: Thomas Monjalon [mailto:thomas.monjalon@6wind.com] > > Sent: Wednesday, June 25, 2014 2:44 PM > > To: Doherty, Declan > > Cc: dev@dpdk.org > > Subject: Re: [PATCH v7 6/6] Link Bonding Library doxygen additions > > > > Hi Declan, > > > > Since bonding library is a PMD, there should have no need of integrate it > > in the API documentation. > > I hadn't time to review it yet, so I would miss something. > > Do you mean it can be used directly by the application with a specific > > API? > > In this case, you could explain it in the commit log. > > Hi Thomas, the bonding library has a API which supports creation and > management of bonded devices directly from within applications, but more > importantly provides the APIs required to dynamically add and remove slaves > from a bonded device, this functionality will be required to support live > migration of VMs using bonded ports in a future releases. OK. Could I suggest to split rte_eth_bond.c in 2 files? 1 file for the library matching the API and 1 file for the PMD layer? I feel it would be easier to read and maintain. Thanks -- Thomas ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v6 1/6] Link Bonding Library (lib/librte_pmd_bond) 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty 2014-06-18 16:18 ` Neil Horman 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty @ 2014-06-24 14:52 ` Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 2/6] Support for unique interface naming of pmds Declan Doherty ` (4 subsequent siblings) 7 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 14:52 UTC (permalink / raw) To: dev Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2142 ++++++++++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 +++++ mk/rte.app.mk | 5 + 7 files changed, 2445 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 989e1da..214398b 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -206,6 +206,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 5b896c3..2bf90df 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -244,6 +244,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/lib/Makefile b/lib/Makefile index c58c0c9..88e875f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -49,6 +49,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..51f6159 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,32 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.c b/lib/librte_pmd_bond/rte_eth_bond.c new file mode 100644 index 0000000..1b0cf81 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.c @@ -0,0 +1,2142 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" + +/* Link Bonding KVARG option defintions for --vdev */ +#define PMD_BOND_KVARG RTE_PMD_BOND + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +static const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int slave_idx = 0; + int i, cs_idx = 0; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + cs_idx = (slave_idx + i) % num_of_slaves; + slave_bufs[cs_idx][(slave_nb_pkts[cs_idx])++] = bufs[i]; + } + + /* increment current slave index so the next call to tx burst starts on the + * next slave */ + slave_idx = ++cs_idx; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) + if (slave_nb_pkts[i] > 0) + num_tx_total += rte_eth_tx_burst(slaves[i], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + + + +static void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + + return port_id; +} +static int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +static int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +static int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +static int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +static int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +static int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} + + +static int +rte_pmd_bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver rte_pmd_bond_drv = { + .name = PMD_BOND_KVARG, + .type = PMD_BDEV, + .init = rte_pmd_bond_init, +}; + +PMD_REGISTER_DRIVER(rte_pmd_bond_drv); + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..7cf9dd8 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 8db9cde..2539f46 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -205,8 +205,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v6 2/6] Support for unique interface naming of pmds 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty ` (2 preceding siblings ...) 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty @ 2014-06-24 14:52 ` Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 3/6] EAL support for link bonding device initialization Declan Doherty ` (3 subsequent siblings) 7 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 14:52 UTC (permalink / raw) To: dev Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_ether/rte_ethdev.c | 32 +++++++++++++++++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 +++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 ++++++++++---------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++-------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 7256841..d938603 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -65,6 +65,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -153,21 +154,40 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static struct rte_eth_dev * +rte_eth_dev_allocated(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return &rte_eth_devices[i]; + } + return NULL; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; if (nb_ports == RTE_MAX_ETHPORTS) { - PMD_DEBUG_TRACE("Reached maximum number of ethernet ports\n"); + PMD_DEBUG_TRACE("Reached maximum number of Ethernet ports\n"); return NULL; } if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_allocated(name) != NULL) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + rte_snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), + "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } @@ -178,11 +198,17 @@ rte_eth_dev_init(struct rte_pci_driver *pci_drv, { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique Ethernet device name using PCI address */ + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 2406e45..50df654 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1497,6 +1497,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1505,6 +1507,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1560,10 +1564,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index 10d4e24..f9174f1 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - rte_snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + rte_snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - rte_snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + rte_snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 7c4d3fe..a0b4bdd 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v6 3/6] EAL support for link bonding device initialization 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty ` (3 preceding siblings ...) 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 2/6] Support for unique interface naming of pmds Declan Doherty @ 2014-06-24 14:52 ` Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 4/6] Link bonding Unit Tests Declan Doherty ` (2 subsequent siblings) 7 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 14:52 UTC (permalink / raw) To: dev Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_eal/bsdapp/eal/eal.c | 10 ++++- lib/librte_eal/common/eal_common_dev.c | 58 ++++++++++++++++++-------- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 --- lib/librte_eal/common/include/rte_dev.h | 13 +++++- lib/librte_eal/linuxapp/eal/eal.c | 11 +++++- 6 files changed, 73 insertions(+), 29 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index a1f014f..c53f63e 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -874,7 +874,7 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -906,6 +906,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..8e80093 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) } int -rte_eal_dev_init(void) +rte_eal_dev_init(uint8_t init_pri) { struct rte_devargs *devargs; struct rte_driver *driver; @@ -80,30 +80,52 @@ rte_eal_dev_init(void) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) - continue; + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, + * virtual devices are initialized pre PCI probing and bonded + * device are post pci probing */ + if ((driver->type == PMD_VDEV && init_pri == + PMD_INIT_PRE_PCI_PROBE) || + (driver->type == PMD_BDEV && init_pri == + PMD_INIT_POST_PCI_PROBE)) { - /* search a driver prefix in virtual device name */ - if (!strncmp(driver->name, devargs->virtual.drv_name, - strlen(driver->name))) { - driver->init(devargs->virtual.drv_name, - devargs->args); - break; + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + printf("init (%u) %s\n", init_pri, devargs->virtual.drv_name); + driver->init(devargs->virtual.drv_name, + devargs->args); + break; + } } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + /* If initializing pre PCI probe, then we don't expect a bonded driver + * to be found */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) != 0) { + if (driver == NULL) { + rte_panic("no driver found for virtual device %s\n", + devargs->virtual.drv_name); + } + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) == 0) { + if (driver == NULL) { + rte_panic("no driver found for bonded device %s\n", + devargs->virtual.drv_name); + } } } - /* Once the vdevs are initalized, start calling all the pdev drivers */ - TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_PDEV) - continue; - /* PDEV drivers don't get passed any parameters */ - driver->init(NULL, NULL); + /* Once the vdevs are initialized, start calling all the pdev drivers */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_PDEV) + continue; + /* PDEV drivers don't get passed any parameters */ + driver->init(NULL, NULL); + } } return 0; } diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index af809a8..c637361 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -150,6 +150,9 @@ rte_eal_pci_probe(void) probe_all = 1; TAILQ_FOREACH(dev, &pci_device_list, next) { + /* check if device has already been initialized */ + if (dev->driver != NULL) + continue; /* set devargs in PCI structure */ devargs = pci_devargs_lookup(dev); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..b440ffb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -196,11 +196,4 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); -/** - * This function initialises any virtual devices - * - * This function is private to the EAL. - */ -int rte_eal_dev_init(void); - #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..eaf3284 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,15 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ +}; + +#define RTE_PMD_BOND ("eth_bond") +/** + * Driver initialization */ +enum pmd_init_priority { + PMD_INIT_PRE_PCI_PROBE = 0, + PMD_INIT_POST_PCI_PROBE = 1, }; /** @@ -93,9 +102,9 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** - * Initalize all the registered drivers in this process + * Initialize all the registered drivers in this process */ -int rte_eal_dev_init(void); +int rte_eal_dev_init(uint8_t init_priority); #define PMD_REGISTER_DRIVER(d)\ void devinitfn_ ##d(void);\ diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index d204387..573fd06 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -75,6 +75,7 @@ #include <rte_atomic.h> #include <malloc_heap.h> #include <rte_eth_ring.h> +#include <rte_dev.h> #include "eal_private.h" #include "eal_thread.h" @@ -1097,7 +1098,7 @@ rte_eal_init(int argc, char **argv) RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", rte_config.master_lcore, (int)thread_id); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -1127,6 +1128,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v6 4/6] Link bonding Unit Tests 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty ` (4 preceding siblings ...) 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 3/6] EAL support for link bonding device initialization Declan Doherty @ 2014-06-24 14:52 ` Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 5/6] testpmd link bonding additions Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 6/6] Link Bonding Library doxygen additions Declan Doherty 7 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 14:52 UTC (permalink / raw) To: dev Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4982 insertions(+), 1 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 9c52460..643f1b9 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -102,7 +102,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index c9dc085..5f23420 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -159,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -227,6 +231,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..5d539f1 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,287 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index c54e7ef..c00e1d6 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -97,6 +97,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..6662059 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v6 5/6] testpmd link bonding additions 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty ` (5 preceding siblings ...) 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 4/6] Link bonding Unit Tests Declan Doherty @ 2014-06-24 14:52 ` Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 6/6] Link Bonding Library doxygen additions Declan Doherty 7 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 14:52 UTC (permalink / raw) To: dev - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 579 +++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 619 insertions(+), 9 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 3298360..fcc6449 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -404,6 +407,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -3031,6 +3059,547 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static int bond_dev_num = 0; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int port_id; + + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "eth_bond_testpmd_%d", + bond_dev_num++); + + /* Create a new bonded device. */ + port_id = rte_eth_bond_create(ethdev_name, res->mode, res->socket); + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("Created new bonded device %s on (port %d).\n", ethdev_name, + port_id); + + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -6572,6 +7141,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 255f82d..38a93a1 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -252,6 +252,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -265,7 +266,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 3ff4f81..69b47e8 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 546d429..1e55815 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1252,7 +1278,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1380,10 +1406,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; @@ -1826,9 +1853,6 @@ main(int argc, char** argv) if (diag < 0) rte_panic("Cannot init EAL\n"); - if (rte_eal_pci_probe()) - rte_panic("Cannot probe PCI\n"); - nb_ports = (portid_t) rte_eth_dev_count(); if (nb_ports == 0) rte_exit(EXIT_FAILURE, "No probed ethernet devices - " diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 5839f93..b8288c1 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); void port_mtu_set(portid_t port_id, uint16_t mtu); -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v6 6/6] Link Bonding Library doxygen additions 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty ` (6 preceding siblings ...) 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 5/6] testpmd link bonding additions Declan Doherty @ 2014-06-24 14:52 ` Declan Doherty 7 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-24 14:52 UTC (permalink / raw) To: dev Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 7b26e98..ee3ad4f 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_eth_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index f380d9a..b15a340 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_pmd_bond \ lib/librte_eal/common/include \ lib/librte_acl \ lib/librte_distributor \ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v5 1/6] Link Bonding Library (lib/librte_pmd_bond) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty @ 2014-06-18 16:14 ` Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 2/6] Support for unique interface naming of pmds Declan Doherty ` (4 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-18 16:14 UTC (permalink / raw) To: dev Initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, layer 3+4) Mode 3 - Broadcast Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2148 ++++++++++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 +++++ mk/rte.app.mk | 5 + 7 files changed, 2451 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index 989e1da..214398b 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -206,6 +206,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 5b896c3..2bf90df 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -244,6 +244,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/lib/Makefile b/lib/Makefile index c58c0c9..88e875f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -49,6 +49,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..51f6159 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,32 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.c b/lib/librte_pmd_bond/rte_eth_bond.c new file mode 100644 index 0000000..69e7bae --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.c @@ -0,0 +1,2148 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" + +/* Link Bonding KVARG option defintions for --vdev */ +#define PMD_BOND_KVARG RTE_PMD_BOND + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +static const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int last_slave_idx = -1; + int i, slave_idx; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + slave_idx = i % num_of_slaves; + slave_bufs[slave_idx][(slave_nb_pkts[slave_idx])++] = bufs[i]; + } + + /* calculate the next slave to transmit on based on the last slave idx used + * in the last call to bond_ethdev_tx_burst_round_robin */ + slave_idx = last_slave_idx + 1; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + slave_idx = (slave_idx + i) % num_of_slaves; + + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[slave_idx], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + } + } + + last_slave_idx = slave_idx; + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + + + +static void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + + return port_id; +} +static int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +static int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +static int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +static int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +static int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +static int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} + + +static int +rte_pmd_bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver rte_pmd_bond_drv = { + .name = PMD_BOND_KVARG, + .type = PMD_BDEV, + .init = rte_pmd_bond_init, +}; + +PMD_REGISTER_DRIVER(rte_pmd_bond_drv); + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..7cf9dd8 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 8db9cde..2539f46 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -205,8 +205,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v5 2/6] Support for unique interface naming of pmds 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty @ 2014-06-18 16:14 ` Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 3/6] EAL support for link bonding device initialization Declan Doherty ` (3 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-18 16:14 UTC (permalink / raw) To: dev Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_ether/rte_ethdev.c | 32 +++++++++++++++++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 +++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 ++++++++++---------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++-------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 7256841..d938603 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -65,6 +65,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -153,21 +154,40 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static struct rte_eth_dev * +rte_eth_dev_allocated(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return &rte_eth_devices[i]; + } + return NULL; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; if (nb_ports == RTE_MAX_ETHPORTS) { - PMD_DEBUG_TRACE("Reached maximum number of ethernet ports\n"); + PMD_DEBUG_TRACE("Reached maximum number of Ethernet ports\n"); return NULL; } if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_allocated(name) != NULL) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + rte_snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), + "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } @@ -178,11 +198,17 @@ rte_eth_dev_init(struct rte_pci_driver *pci_drv, { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique Ethernet device name using PCI address */ + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 2406e45..50df654 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1497,6 +1497,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1505,6 +1507,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1560,10 +1564,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index 10d4e24..f9174f1 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - rte_snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + rte_snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - rte_snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + rte_snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 7c4d3fe..a0b4bdd 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v5 3/6] EAL support for link bonding device initialization 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty ` (2 preceding siblings ...) 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 2/6] Support for unique interface naming of pmds Declan Doherty @ 2014-06-18 16:14 ` Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 4/6] Link bonding Unit Tests Declan Doherty ` (2 subsequent siblings) 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-18 16:14 UTC (permalink / raw) To: dev Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_eal/bsdapp/eal/eal.c | 10 ++++- lib/librte_eal/common/eal_common_dev.c | 58 ++++++++++++++++++-------- lib/librte_eal/common/eal_common_pci.c | 3 + lib/librte_eal/common/include/eal_private.h | 7 --- lib/librte_eal/common/include/rte_dev.h | 13 +++++- lib/librte_eal/linuxapp/eal/eal.c | 11 +++++- 6 files changed, 73 insertions(+), 29 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index a1f014f..c53f63e 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -874,7 +874,7 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -906,6 +906,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..8e80093 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -62,7 +62,7 @@ rte_eal_driver_unregister(struct rte_driver *driver) } int -rte_eal_dev_init(void) +rte_eal_dev_init(uint8_t init_pri) { struct rte_devargs *devargs; struct rte_driver *driver; @@ -80,30 +80,52 @@ rte_eal_dev_init(void) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) - continue; + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device, + * virtual devices are initialized pre PCI probing and bonded + * device are post pci probing */ + if ((driver->type == PMD_VDEV && init_pri == + PMD_INIT_PRE_PCI_PROBE) || + (driver->type == PMD_BDEV && init_pri == + PMD_INIT_POST_PCI_PROBE)) { - /* search a driver prefix in virtual device name */ - if (!strncmp(driver->name, devargs->virtual.drv_name, - strlen(driver->name))) { - driver->init(devargs->virtual.drv_name, - devargs->args); - break; + /* search a driver prefix in virtual device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + printf("init (%u) %s\n", init_pri, devargs->virtual.drv_name); + driver->init(devargs->virtual.drv_name, + devargs->args); + break; + } } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + /* If initializing pre PCI probe, then we don't expect a bonded driver + * to be found */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) != 0) { + if (driver == NULL) { + rte_panic("no driver found for virtual device %s\n", + devargs->virtual.drv_name); + } + } else if (init_pri == PMD_INIT_POST_PCI_PROBE && + strncmp(RTE_PMD_BOND, devargs->virtual.drv_name, + strlen(RTE_PMD_BOND)) == 0) { + if (driver == NULL) { + rte_panic("no driver found for bonded device %s\n", + devargs->virtual.drv_name); + } } } - /* Once the vdevs are initalized, start calling all the pdev drivers */ - TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_PDEV) - continue; - /* PDEV drivers don't get passed any parameters */ - driver->init(NULL, NULL); + /* Once the vdevs are initialized, start calling all the pdev drivers */ + if (init_pri == PMD_INIT_PRE_PCI_PROBE) { + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_PDEV) + continue; + /* PDEV drivers don't get passed any parameters */ + driver->init(NULL, NULL); + } } return 0; } diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index af809a8..c637361 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -150,6 +150,9 @@ rte_eal_pci_probe(void) probe_all = 1; TAILQ_FOREACH(dev, &pci_device_list, next) { + /* check if device has already been initialized */ + if (dev->driver != NULL) + continue; /* set devargs in PCI structure */ devargs = pci_devargs_lookup(dev); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..b440ffb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -196,11 +196,4 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); -/** - * This function initialises any virtual devices - * - * This function is private to the EAL. - */ -int rte_eal_dev_init(void); - #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..eaf3284 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,15 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ +}; + +#define RTE_PMD_BOND ("eth_bond") +/** + * Driver initialization */ +enum pmd_init_priority { + PMD_INIT_PRE_PCI_PROBE = 0, + PMD_INIT_POST_PCI_PROBE = 1, }; /** @@ -93,9 +102,9 @@ void rte_eal_driver_register(struct rte_driver *driver); void rte_eal_driver_unregister(struct rte_driver *driver); /** - * Initalize all the registered drivers in this process + * Initialize all the registered drivers in this process */ -int rte_eal_dev_init(void); +int rte_eal_dev_init(uint8_t init_priority); #define PMD_REGISTER_DRIVER(d)\ void devinitfn_ ##d(void);\ diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index 6994303..7be13fe 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -75,6 +75,7 @@ #include <rte_atomic.h> #include <malloc_heap.h> #include <rte_eth_ring.h> +#include <rte_dev.h> #include "eal_private.h" #include "eal_thread.h" @@ -1096,7 +1097,7 @@ rte_eal_init(int argc, char **argv) RTE_LOG(DEBUG, EAL, "Master core %u is ready (tid=%x)\n", rte_config.master_lcore, (int)thread_id); - if (rte_eal_dev_init() < 0) + if (rte_eal_dev_init(PMD_INIT_PRE_PCI_PROBE) < 0) rte_panic("Cannot init pmd devices\n"); RTE_LCORE_FOREACH_SLAVE(i) { @@ -1126,6 +1127,14 @@ rte_eal_init(int argc, char **argv) rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); + /* Probe & Initialize PCI devices */ + if (rte_eal_pci_probe()) + rte_panic("Cannot probe PCI\n"); + + /* Initialize any outstanding devices */ + if (rte_eal_dev_init(PMD_INIT_POST_PCI_PROBE) < 0) + rte_panic("Cannot init pmd devices\n"); + return fctret; } -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v5 4/6] Link bonding Unit Tests 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty ` (3 preceding siblings ...) 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 3/6] EAL support for link bonding device initialization Declan Doherty @ 2014-06-18 16:14 ` Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 5/6] testpmd link bonding additions Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 6/6] Link Bonding Library doxygen additions Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-18 16:14 UTC (permalink / raw) To: dev Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 287 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4982 insertions(+), 1 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 9c52460..643f1b9 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -102,7 +102,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index c9dc085..5f23420 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -159,6 +159,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -227,6 +231,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..5d539f1 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,287 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index c54e7ef..c00e1d6 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -97,6 +97,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..6662059 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v5 5/6] testpmd link bonding additions 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty ` (4 preceding siblings ...) 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 4/6] Link bonding Unit Tests Declan Doherty @ 2014-06-18 16:14 ` Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 6/6] Link Bonding Library doxygen additions Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-18 16:14 UTC (permalink / raw) To: dev - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 579 +++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 40 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 619 insertions(+), 9 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index e3e51fc..967c058 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -404,6 +407,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -3031,6 +3059,547 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static int bond_dev_num = 0; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int port_id; + + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "eth_bond_testpmd_%d", + bond_dev_num++); + + /* Create a new bonded device. */ + port_id = rte_eth_bond_create(ethdev_name, res->mode, res->socket); + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("Created new bonded device %s on (port %d).\n", ethdev_name, + port_id); + + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -6572,6 +7141,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 0023ab2..c9d4a85 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -252,6 +252,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -265,7 +266,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index aa0e2bf..097aacf 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 546d429..1e55815 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1252,7 +1278,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1380,10 +1406,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; @@ -1826,9 +1853,6 @@ main(int argc, char** argv) if (diag < 0) rte_panic("Cannot init EAL\n"); - if (rte_eal_pci_probe()) - rte_panic("Cannot probe PCI\n"); - nb_ports = (portid_t) rte_eth_dev_count(); if (nb_ports == 0) rte_exit(EXIT_FAILURE, "No probed ethernet devices - " diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 5839f93..b8288c1 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); void port_mtu_set(portid_t port_id, uint16_t mtu); -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v5 6/6] Link Bonding Library doxygen additions 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty ` (5 preceding siblings ...) 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 5/6] testpmd link bonding additions Declan Doherty @ 2014-06-18 16:14 ` Declan Doherty 6 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-18 16:14 UTC (permalink / raw) To: dev Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 7b26e98..ee3ad4f 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_eth_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index f380d9a..b15a340 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_pmd_bond \ lib/librte_eal/common/include \ lib/librte_acl \ lib/librte_distributor \ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v4 1/6] Link Bonding Library (lib/librte_pmd_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, la Mode 3 - Broadcast 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (6 preceding siblings ...) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty @ 2014-06-16 11:18 ` Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 2/6] Support for unique interface naming of pmds Declan Doherty ` (4 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-16 11:18 UTC (permalink / raw) To: dev Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- config/common_bsdapp | 5 + config/common_linuxapp | 5 + lib/Makefile | 1 + lib/librte_pmd_bond/Makefile | 32 + lib/librte_pmd_bond/rte_eth_bond.c | 2149 ++++++++++++++++++++++++++++++++++++ lib/librte_pmd_bond/rte_eth_bond.h | 255 +++++ mk/rte.app.mk | 5 + 7 files changed, 2452 insertions(+), 0 deletions(-) create mode 100644 lib/librte_pmd_bond/Makefile create mode 100644 lib/librte_pmd_bond/rte_eth_bond.c create mode 100644 lib/librte_pmd_bond/rte_eth_bond.h diff --git a/config/common_bsdapp b/config/common_bsdapp index ef8eeab..6846cba 100644 --- a/config/common_bsdapp +++ b/config/common_bsdapp @@ -189,6 +189,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=y # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Do prefetch of packet data within PMD driver receive function # CONFIG_RTE_PMD_PACKET_PREFETCH=y diff --git a/config/common_linuxapp b/config/common_linuxapp index 9f93569..3039591 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -218,6 +218,11 @@ CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16 CONFIG_RTE_LIBRTE_PMD_PCAP=n # +# Compile link bonding pmd library +# +CONFIG_RTE_LIBRTE_PMD_BOND=y + +# # Compile Xen PMD # CONFIG_RTE_LIBRTE_PMD_XENVIRT=n diff --git a/lib/Makefile b/lib/Makefile index a9f94b4..5b025c7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -47,6 +47,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PMD_PCAP) += librte_pmd_pcap DIRS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += librte_pmd_virtio DIRS-$(CONFIG_RTE_LIBRTE_VMXNET3_PMD) += librte_pmd_vmxnet3 DIRS-$(CONFIG_RTE_LIBRTE_PMD_XENVIRT) += librte_pmd_xenvirt +DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += librte_pmd_bond DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl diff --git a/lib/librte_pmd_bond/Makefile b/lib/librte_pmd_bond/Makefile new file mode 100644 index 0000000..51f6159 --- /dev/null +++ b/lib/librte_pmd_bond/Makefile @@ -0,0 +1,32 @@ +# <COPYRIGHT_TAG> + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_bond.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += rte_eth_bond.c + + +# +# Export include files +# +SYMLINK-y-include += rte_eth_bond.h + + +# this lib depends upon: +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_mbuf +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_ether +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_malloc +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_eal +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += lib/librte_kvargs + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_pmd_bond/rte_eth_bond.c b/lib/librte_pmd_bond/rte_eth_bond.c new file mode 100644 index 0000000..01d917d --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.c @@ -0,0 +1,2149 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <linux/binfmts.h> + +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_devargs.h> +#include <rte_ethdev.h> +#include <rte_ip.h> +#include <rte_kvargs.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> +#include <rte_udp.h> + +#include <cmdline_parse.h> +#include <cmdline_parse_etheraddr.h> + +#include "rte_eth_bond.h" + +/* Link Bonding KVARG option defintions for --vdev */ +#define PMD_BOND_KVARG ("eth_bond") + +#define PMD_BOND_SLAVE_PORT_KVARG ("slave") +#define PMD_BOND_PRIMARY_SLAVE_KVARG ("primary") +#define PMD_BOND_MODE_KVARG ("mode") +#define PMD_BOND_XMIT_POLICY_KVARG ("xmit_policy") +#define PMD_BOND_SOCKET_ID_KVARG ("socket_id") +#define PMD_BOND_MAC_ADDR_KVARG ("mac") + +#define PMD_BOND_XMIT_POLICY_LAYER2_KVARG ("l2") +#define PMD_BOND_XMIT_POLICY_LAYER23_KVARG ("l23") +#define PMD_BOND_XMIT_POLICY_LAYER34_KVARG ("l34") + +static const char *pmd_bond_init_valid_arguments[] = { + PMD_BOND_SLAVE_PORT_KVARG, + PMD_BOND_PRIMARY_SLAVE_KVARG, + PMD_BOND_MODE_KVARG, + PMD_BOND_XMIT_POLICY_KVARG, + PMD_BOND_SOCKET_ID_KVARG, + PMD_BOND_MAC_ADDR_KVARG, + + NULL +}; + +static const char *driver_name = "Link Bonding PMD"; + +/** Port Queue Mapping Structure */ +struct bond_rx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to eth_dev private structure */ + uint16_t nb_rx_desc; + /**< Number of RX descriptors available for the queue */ + struct rte_eth_rxconf rx_conf; + /**< Copy of RX configuration structure for queue */ + struct rte_mempool *mb_pool; + /**< Reference to mbuf pool to use for RX queue */ +}; + +struct bond_tx_queue { + int queue_id; + /**< Queue Id */ + struct bond_dev_private *dev_private; + /**< Reference to dev private structure */ + uint16_t nb_tx_desc; + /**< Number of TX descriptors available for the queue */ + struct rte_eth_txconf tx_conf; + /**< Copy of TX configuration structure for queue */ +}; + + +/** Persisted Slave Configuration Structure */ +struct slave_conf { + uint8_t port_id; + /**< Port Id of slave eth_dev */ + struct ether_addr mac_addr; + /**< Slave eth_dev original MAC address */ +}; +/** Bonded slave devices structure */ +struct bond_ethdev_slave_ports { + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave port id array */ + uint8_t slave_count; /**< Number of slaves */ +}; + +/** Link Bonding PMD device private configuration Structure */ +struct bond_dev_private { + uint8_t mode; /**< Link Bonding Mode */ + + uint8_t primary_port; /**< Primary Slave Port */ + uint8_t current_primary_port; /**< Primary Slave Port */ + uint8_t user_defined_primary_port; + /**< Flag for whether primary port is user defined or not */ + uint8_t balance_xmit_policy; + /**< Transmit policy - l2 / l23 / l34 for operation in balance mode */ + uint8_t user_defined_mac; + /**< Flag for whether MAC address is user defined or not */ + uint8_t promiscuous_en; + /**< Enabled/disable promiscuous mode on slave devices */ + uint8_t link_props_set; + /**< Bonded eth_dev link properties set */ + + uint16_t nb_rx_queues; /**< Total number of rx queues */ + uint16_t nb_tx_queues; /**< Total number of tx queues*/ + + uint8_t slave_count; /**< Number of active slaves */ + uint8_t active_slave_count; /**< Number of slaves */ + + uint8_t active_slaves[RTE_MAX_ETHPORTS]; /**< Active slave list */ + uint8_t slaves[RTE_MAX_ETHPORTS]; /**< Slave list */ + + /** Persisted configuration of slaves */ + struct slave_conf presisted_slaves_conf[RTE_MAX_ETHPORTS]; +}; + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id); + +static int +valid_bonded_ethdev(struct rte_eth_dev *eth_dev) +{ + size_t len; + + /* Check valid pointer */ + if (eth_dev->driver->pci_drv.name == NULL || driver_name == NULL) + return -1; + + /* Check string lengths are equal */ + len = strlen(driver_name); + if (strlen(eth_dev->driver->pci_drv.name) != len) + return -1; + + /* Compare strings */ + return strncmp(eth_dev->driver->pci_drv.name, driver_name, len); +} + +static int +valid_port_id(uint8_t port_id) +{ + /* Verify that port id is valid */ + int ethdev_count = rte_eth_dev_count(); + if (port_id >= ethdev_count) { + RTE_LOG(ERR, PMD, + "%s: port Id %d is greater than rte_eth_dev_count %d\n", + __func__, port_id, ethdev_count); + return -1; + } + + return 0; +} + +static int +valid_bonded_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that bonded_port_id refers to a bonded port */ + if (valid_bonded_ethdev(&rte_eth_devices[port_id])) { + RTE_LOG(ERR, PMD, + "%s: Specified port Id %d is not a bonded eth_dev device\n", + __func__, port_id); + return -1; + } + + return 0; +} + +static int +valid_slave_port_id(uint8_t port_id) +{ + /* Verify that port id's are valid */ + if (valid_port_id(port_id)) + return -1; + + /* Verify that port_id refers to a non bonded port */ + if (!valid_bonded_ethdev(&rte_eth_devices[port_id])) + return -1; + + return 0; +} + + +static uint16_t +bond_ethdev_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + + uint16_t num_rx_slave = 0; + uint16_t num_rx_total = 0; + + int i; + + /* Cast to structure, containing bonded device's port id and queue id */ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *)queue; + + internals = bd_rx_q->dev_private; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BROADCAST: + case BONDING_MODE_BALANCE: + for (i = 0; i < internals->active_slave_count; i++) { + /* Offset of pointer to *bufs increases as packets are received + * from other slaves */ + num_rx_slave = rte_eth_rx_burst(internals->active_slaves[i], + bd_rx_q->queue_id, bufs + num_rx_total, nb_pkts); + if (num_rx_slave) + num_rx_total += num_rx_slave; + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + num_rx_slave = rte_eth_rx_burst(internals->current_primary_port, + bd_rx_q->queue_id, bufs, nb_pkts); + if (num_rx_slave) + num_rx_total = num_rx_slave; + break; + } + return num_rx_total; +} + + +static uint16_t +bond_ethdev_tx_round_robin(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *dev_private; + struct bond_tx_queue *bd_tx_q; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + static int last_slave_idx = -1; + int i, slave_idx; + + bd_tx_q = (struct bond_tx_queue *)queue; + dev_private = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = dev_private->active_slave_count; + memcpy(slaves, dev_private->active_slaves, + sizeof(dev_private->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + /* Populate slaves mbuf with which packets are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + slave_idx = i % num_of_slaves; + slave_bufs[slave_idx][(slave_nb_pkts[slave_idx])++] = bufs[i]; + } + + /* calculate the next slave to transmit on based on the last slave idx used + * in the last call to bond_ethdev_tx_burst_round_robin */ + slave_idx = last_slave_idx + 1; + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + slave_idx = (slave_idx + i) % num_of_slaves; + + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[slave_idx], + bd_tx_q->queue_id, slave_bufs[i], slave_nb_pkts[i]); + } + } + + last_slave_idx = slave_idx; + + return num_tx_total; +} + +static uint16_t bond_ethdev_tx_active_backup(void *queue, + struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + if (internals->active_slave_count < 1) + return 0; + + return rte_eth_tx_burst(internals->current_primary_port, bd_tx_q->queue_id, + bufs, nb_pkts); +} + + +static inline uint16_t +ether_hash(struct ether_hdr *eth_hdr) +{ + uint16_t *word_src_addr = (uint16_t *)eth_hdr->s_addr.addr_bytes; + uint16_t *word_dst_addr = (uint16_t *)eth_hdr->d_addr.addr_bytes; + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]); +} + +static inline uint32_t +ipv4_hash(struct ipv4_hdr *ipv4_hdr) +{ + return (ipv4_hdr->src_addr ^ ipv4_hdr->dst_addr); +} + +static inline uint32_t +ipv6_hash(struct ipv6_hdr *ipv6_hdr) +{ + uint32_t *word_src_addr = (uint32_t *)&(ipv6_hdr->src_addr[0]); + uint32_t *word_dst_addr = (uint32_t *)&(ipv6_hdr->dst_addr[0]); + + return (word_src_addr[0] ^ word_dst_addr[0]) ^ + (word_src_addr[1] ^ word_dst_addr[1]) ^ + (word_src_addr[2] ^ word_dst_addr[2]) ^ + (word_src_addr[3] ^ word_dst_addr[3]); +} + +static uint32_t +udp_hash(struct udp_hdr *hdr) +{ + return hdr->src_port ^ hdr->dst_port; +} + +static inline uint16_t +xmit_slave_hash(const struct rte_mbuf *buf, uint8_t slave_count, uint8_t policy) +{ + struct ether_hdr *eth_hdr; + struct udp_hdr *udp_hdr; + size_t eth_offset = 0; + uint32_t hash = 0; + + if (slave_count == 1) + return 0; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + hash = ether_hash(eth_hdr); + hash ^= hash >> 8; + return hash % slave_count; + + + case BALANCE_XMIT_POLICY_LAYER23: + eth_hdr = (struct ether_hdr *)buf->pkt.data; + + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr; + ipv4_hdr = (struct ipv4_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv4_hash(ipv4_hdr); + + } else { + struct ipv6_hdr *ipv6_hdr; + + ipv6_hdr = (struct ipv6_hdr *)(rte_pktmbuf_mtod(buf, + unsigned char *) + eth_offset); + + hash = ether_hash(eth_hdr) ^ ipv6_hash(ipv6_hdr); + } + break; + + case BALANCE_XMIT_POLICY_LAYER34: + if (buf->ol_flags & PKT_RX_VLAN_PKT) + eth_offset = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_offset = sizeof(struct ether_hdr); + + if (buf->ol_flags & PKT_RX_IPV4_HDR) { + struct ipv4_hdr *ipv4_hdr = (struct ipv4_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv4_hdr)); + hash = ipv4_hash(ipv4_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv4_hash(ipv4_hdr); + } + } else { + struct ipv6_hdr *ipv6_hdr = (struct ipv6_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset); + + if (ipv6_hdr->proto == IPPROTO_UDP) { + udp_hdr = (struct udp_hdr *) + (rte_pktmbuf_mtod(buf, unsigned char *) + eth_offset + + sizeof(struct ipv6_hdr)); + hash = ipv6_hash(ipv6_hdr) ^ udp_hash(udp_hdr); + } else { + hash = ipv6_hash(ipv6_hdr); + } + } + break; + } + + hash ^= hash >> 16; + hash ^= hash >> 8; + + return hash % slave_count; +} + +static uint16_t +bond_ethdev_tx_balance(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i, op_slave_id; + + struct rte_mbuf *slave_bufs[RTE_MAX_ETHPORTS][nb_pkts]; + uint16_t slave_nb_pkts[RTE_MAX_ETHPORTS] = { 0 }; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return num_tx_total; + + + /* Populate slaves mbuf with the packets which are to be sent on it */ + for (i = 0; i < nb_pkts; i++) { + /* Select output slave using hash based on xmit policy */ + op_slave_id = xmit_slave_hash(bufs[i], num_of_slaves, + internals->balance_xmit_policy); + + /* Populate slave mbuf arrays with mbufs for that slave */ + slave_bufs[op_slave_id][slave_nb_pkts[op_slave_id]++] = bufs[i]; + } + + /* Send packet burst on each slave device */ + for (i = 0; i < num_of_slaves; i++) { + if (slave_nb_pkts[i] > 0) { + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + slave_bufs[i], slave_nb_pkts[i]); + } + } + + return num_tx_total; +} + +static uint16_t +bond_ethdev_tx_burst_broadcast(void *queue, struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct bond_dev_private *internals; + struct bond_tx_queue *bd_tx_q; + + uint8_t num_of_slaves; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + uint16_t num_tx_total = 0; + + int i; + + bd_tx_q = (struct bond_tx_queue *)queue; + internals = bd_tx_q->dev_private; + + /* Copy slave list to protect against slave up/down changes during tx + * bursting */ + num_of_slaves = internals->active_slave_count; + memcpy(slaves, internals->active_slaves, + sizeof(internals->active_slaves[0]) * num_of_slaves); + + if (num_of_slaves < 1) + return 0; + + /* Increment reference count on mbufs */ + for (i = 0; i < nb_pkts; i++) + rte_mbuf_refcnt_update(bufs[i], num_of_slaves - 1); + + /* Transmit burst on each active slave */ + for (i = 0; i < num_of_slaves; i++) + num_tx_total += rte_eth_tx_burst(slaves[i], bd_tx_q->queue_id, + bufs, nb_pkts); + + return num_tx_total; +} + +static void +link_properties_set(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_link *slave_dev_link) +{ + struct rte_eth_link *bonded_dev_link = &bonded_eth_dev->data->dev_link; + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (slave_dev_link->link_status && + bonded_eth_dev->data->dev_started) { + bonded_dev_link->link_duplex = slave_dev_link->link_duplex; + bonded_dev_link->link_speed = slave_dev_link->link_speed; + + internals->link_props_set = 1; + } +} + +static void +link_properties_reset(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + memset(&(bonded_eth_dev->data->dev_link), 0, + sizeof(bonded_eth_dev->data->dev_link)); + + internals->link_props_set = 0; +} + +static int +link_properties_valid(struct rte_eth_link *bonded_dev_link, + struct rte_eth_link *slave_dev_link) +{ + if (bonded_dev_link->link_duplex != slave_dev_link->link_duplex || + bonded_dev_link->link_speed != slave_dev_link->link_speed) + return -1; + + return 0; +} + +static int +mac_address_set(struct rte_eth_dev *eth_dev, struct ether_addr *new_mac_addr) +{ + struct ether_addr *mac_addr; + + mac_addr = eth_dev->data->mac_addrs; + + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer eth_dev specified\n", __func__); + return -1; + } + + if (new_mac_addr == NULL) { + RTE_LOG(ERR, PMD, "%s: NULL pointer MAC specified\n", __func__); + return -1; + } + + /* if new MAC is different to current MAC then update */ + if (memcmp(mac_addr, new_mac_addr, sizeof(*mac_addr)) != 0) + memcpy(mac_addr, new_mac_addr, sizeof(*mac_addr)); + + return 0; +} + +static int +mac_address_slaves_update(struct rte_eth_dev *bonded_eth_dev) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + int i; + + /* Update slave devices MAC addresses */ + if (internals->slave_count < 1) + return -1; + + switch (internals->mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) { + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + return -1; + } + } + break; + case BONDING_MODE_ACTIVE_BACKUP: + default: + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == internals->current_primary_port) { + if (mac_address_set(&rte_eth_devices[internals->primary_port], + bonded_eth_dev->data->mac_addrs)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->current_primary_port); + } + } else { + struct slave_conf *conf = + slave_config_get(internals, internals->slaves[i]); + + if (mac_address_set(&rte_eth_devices[internals->slaves[i]], + &conf->mac_addr)) { + RTE_LOG(ERR, PMD, + "%s: Failed to update port Id %d MAC address\n", + __func__, internals->slaves[i]); + + + return -1; + } + } + } + } + + return 0; +} + + +static int +bond_ethdev_mode_set(struct rte_eth_dev *eth_dev, int mode) +{ + struct bond_dev_private *internals; + + internals = eth_dev->data->dev_private; + + switch (mode) { + case BONDING_MODE_ROUND_ROBIN: + eth_dev->tx_pkt_burst = bond_ethdev_tx_round_robin; + break; + case BONDING_MODE_ACTIVE_BACKUP: + eth_dev->tx_pkt_burst = bond_ethdev_tx_active_backup; + break; + case BONDING_MODE_BALANCE: + eth_dev->tx_pkt_burst = bond_ethdev_tx_balance; + break; + case BONDING_MODE_BROADCAST: + eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast; + break; + default: + return -1; + } + internals->mode = mode; + + return 0; +} + +static int +slave_configure(struct rte_eth_dev *bonded_eth_dev, + struct rte_eth_dev *slave_eth_dev) +{ + struct bond_rx_queue *bd_rx_q; + struct bond_tx_queue *bd_tx_q; + + int q_id; + + /* Stop slave */ + rte_eth_dev_stop(slave_eth_dev->data->port_id); + + /* Enable interrupts on slave device */ + slave_eth_dev->data->dev_conf.intr_conf.lsc = 1; + + if (rte_eth_dev_configure(slave_eth_dev->data->port_id, + bonded_eth_dev->data->nb_rx_queues, + bonded_eth_dev->data->nb_tx_queues, + &(slave_eth_dev->data->dev_conf)) != 0) { + RTE_LOG(ERR, PMD, "Cannot configure slave device: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + /* Setup Rx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_rx_queues; q_id++) { + bd_rx_q = (struct bond_rx_queue *)bonded_eth_dev->data->rx_queues[q_id]; + + if (rte_eth_rx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_rx_q->nb_rx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &(bd_rx_q->rx_conf), bd_rx_q->mb_pool) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_rx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Setup Tx Queues */ + for (q_id = 0; q_id < bonded_eth_dev->data->nb_tx_queues; q_id++) { + bd_tx_q = (struct bond_tx_queue *)bonded_eth_dev->data->tx_queues[q_id]; + + if (rte_eth_tx_queue_setup(slave_eth_dev->data->port_id, q_id, + bd_tx_q->nb_tx_desc, + rte_eth_dev_socket_id(slave_eth_dev->data->port_id), + &bd_tx_q->tx_conf) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_tx_queue_setup: port=%d queue_id %d\n", + slave_eth_dev->data->port_id, q_id); + return -1; + } + } + + /* Start device */ + if (rte_eth_dev_start(slave_eth_dev->data->port_id) != 0) { + RTE_LOG(ERR, PMD, "rte_eth_dev_start: port=%u\n", + slave_eth_dev->data->port_id); + return -1; + } + + return 0; +} + +static struct slave_conf * +slave_config_get(struct bond_dev_private *internals, uint8_t slave_port_id) +{ + int i; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == slave_port_id) + return &internals->presisted_slaves_conf[i]; + } + return NULL; +} + +static void +slave_config_clear(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + int i, found = 0; + + for (i = 0; i < internals->slave_count; i++) { + if (internals->presisted_slaves_conf[i].port_id == + slave_eth_dev->data->port_id) { + found = 1; + memset(&internals->presisted_slaves_conf[i], 0, + sizeof(internals->presisted_slaves_conf[i])); + } + if (found && i < (internals->slave_count - 1)) { + memcpy(&internals->presisted_slaves_conf[i], + &internals->presisted_slaves_conf[i+1], + sizeof(internals->presisted_slaves_conf[i])); + } + } +} + +static void +slave_config_store(struct bond_dev_private *internals, + struct rte_eth_dev *slave_eth_dev) +{ + struct slave_conf *presisted_slave_conf = + &internals->presisted_slaves_conf[internals->slave_count]; + + presisted_slave_conf->port_id = slave_eth_dev->data->port_id; + + memcpy(&(presisted_slave_conf->mac_addr), slave_eth_dev->data->mac_addrs, + sizeof(struct ether_addr)); +} + + + +static void +bond_ethdev_primary_set(struct bond_dev_private *internals, + uint8_t slave_port_id) +{ + int i; + + if (internals->active_slave_count < 1) + internals->current_primary_port = slave_port_id; + else + /* Search bonded device slave ports for new proposed primary port */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + internals->current_primary_port = slave_port_id; + } +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev); + +static int +bond_ethdev_start(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals; + int i; + + /* slave eth dev will be started by bonded device */ + if (valid_bonded_ethdev(eth_dev)) { + RTE_LOG(ERR, PMD, + "%s: user tried to explicitly start a slave eth_dev (%d) of the bonded eth_dev\n", + __func__, eth_dev->data->port_id); + return -1; + } + + eth_dev->data->dev_link.link_status = 1; + eth_dev->data->dev_started = 1; + + internals = eth_dev->data->dev_private; + + if (internals->slave_count == 0) { + RTE_LOG(ERR, PMD, + "%s: Cannot start port since there are no slave devices\n", + __func__); + return -1; + } + + if (internals->user_defined_mac == 0) { + struct slave_conf *conf = slave_config_get(internals, + internals->primary_port); + + if (mac_address_set(eth_dev, &(conf->mac_addr)) != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to update mac address", + eth_dev->data->port_id); + return -1; + } + } + + /* Update all slave devices MACs*/ + if (mac_address_slaves_update(eth_dev) != 0) + return -1; + + /* If bonded device is configure in promiscuous mode then re-apply config */ + if (internals->promiscuous_en) + bond_ethdev_promiscuous_enable(eth_dev); + + /* Reconfigure each slave device if starting bonded device */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_configure(eth_dev, &(rte_eth_devices[internals->slaves[i]])) + != 0) { + RTE_LOG(ERR, PMD, + "bonded port (%d) failed to reconfigure slave device %d)", + eth_dev->data->port_id, internals->slaves[i]); + return -1; + } + } + + if (internals->user_defined_primary_port) + bond_ethdev_primary_set(internals, internals->primary_port); + + return 0; +} + +static void +bond_ethdev_stop(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + + internals->active_slave_count = 0; + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +bond_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{ +} + +static int +bond_ethdev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static void +bond_ethdev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = dev->pci_dev; +} + +static int +bond_ethdev_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool) +{ + struct bond_rx_queue *bd_rx_q = (struct bond_rx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_rx_queue), + 0, dev->pci_dev->numa_node); + if (bd_rx_q == NULL) + return -1; + + bd_rx_q->queue_id = rx_queue_id; + bd_rx_q->dev_private = dev->data->dev_private; + + bd_rx_q->nb_rx_desc = nb_rx_desc; + + memcpy(&(bd_rx_q->rx_conf), rx_conf, sizeof(struct rte_eth_rxconf)); + bd_rx_q->mb_pool = mb_pool; + + dev->data->rx_queues[rx_queue_id] = bd_rx_q; + + return 0; +} + +static int +bond_ethdev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc, unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf) +{ + struct bond_tx_queue *bd_tx_q = (struct bond_tx_queue *) + rte_zmalloc_socket(NULL, sizeof(struct bond_tx_queue), + 0, dev->pci_dev->numa_node); + + if (bd_tx_q == NULL) + return -1; + + bd_tx_q->queue_id = tx_queue_id; + bd_tx_q->dev_private = dev->data->dev_private; + + bd_tx_q->nb_tx_desc = nb_tx_desc; + memcpy(&(bd_tx_q->tx_conf), tx_conf, sizeof(bd_tx_q->tx_conf)); + + dev->data->tx_queues[tx_queue_id] = bd_tx_q; + + return 0; +} + +static void +bond_ethdev_rx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + +static void +bond_ethdev_tx_queue_release(void *queue) +{ + if (queue == NULL) + return; + + rte_free(queue); +} + + +static int +bond_ethdev_link_update(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete) +{ + struct bond_dev_private *internals = bonded_eth_dev->data->dev_private; + + if (!bonded_eth_dev->data->dev_started || + internals->active_slave_count == 0) { + bonded_eth_dev->data->dev_link.link_status = 0; + return 0; + } else { + struct rte_eth_dev *slave_eth_dev; + int i, link_up = 0; + + for (i = 0; i < internals->active_slave_count; i++) { + slave_eth_dev = &rte_eth_devices[internals->active_slaves[i]]; + + (*slave_eth_dev->dev_ops->link_update)(slave_eth_dev, + wait_to_complete); + if (slave_eth_dev->data->dev_link.link_status == 1) { + link_up = 1; + break; + } + } + + bonded_eth_dev->data->dev_link.link_status = link_up; + } + + return 0; +} + +static void +bond_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct bond_dev_private *internals = dev->data->dev_private; + struct rte_eth_stats slave_stats; + + int i; + + /* clear bonded stats before populating from slaves */ + memset(stats, 0, sizeof(*stats)); + + for (i = 0; i < internals->slave_count; i++) { + rte_eth_stats_get(internals->slaves[i], &slave_stats); + + stats->ipackets += slave_stats.ipackets; + stats->opackets += slave_stats.opackets; + stats->ibytes += slave_stats.ibytes; + stats->obytes += slave_stats.obytes; + stats->ierrors += slave_stats.ierrors; + stats->oerrors += slave_stats.oerrors; + stats->imcasts += slave_stats.imcasts; + stats->rx_nombuf += slave_stats.rx_nombuf; + stats->fdirmatch += slave_stats.fdirmatch; + stats->fdirmiss += slave_stats.fdirmiss; + stats->tx_pause_xon += slave_stats.tx_pause_xon; + stats->rx_pause_xon += slave_stats.rx_pause_xon; + stats->tx_pause_xoff += slave_stats.tx_pause_xoff; + stats->rx_pause_xoff += slave_stats.rx_pause_xoff; + } +} + +static void +bond_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + for (i = 0; i < internals->slave_count; i++) + rte_eth_stats_reset(internals->slaves[i]); +} + +static void +bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev) +{ + struct bond_dev_private *internals = eth_dev->data->dev_private; + int i; + + internals->promiscuous_en = 1; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_enable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_enable(internals->current_primary_port); + + } +} + +static void +bond_ethdev_promiscuous_disable(struct rte_eth_dev *dev) +{ + struct bond_dev_private *internals = dev->data->dev_private; + int i; + + internals->promiscuous_en = 0; + + switch (internals->mode) { + /* Promiscuous mode is propagated to all slaves */ + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + for (i = 0; i < internals->slave_count; i++) + rte_eth_promiscuous_disable(internals->slaves[i]); + break; + /* Promiscuous mode is propagated only to primary slave */ + case BONDING_MODE_ACTIVE_BACKUP: + default: + rte_eth_promiscuous_disable(internals->current_primary_port); + } +} + + +static void +bond_ethdev_lsc_event_callback(uint8_t port_id, enum rte_eth_event_type type, + void *param) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct rte_eth_link link; + + int i, bonded_port_id, valid_slave, active_pos = -1; + + if (type != RTE_ETH_EVENT_INTR_LSC) + return; + + if (param == NULL) + return; + + bonded_port_id = *(uint8_t *)param; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + slave_eth_dev = &rte_eth_devices[port_id]; + + if (valid_bonded_ethdev(bonded_eth_dev)) + return; + + internals = bonded_eth_dev->data->dev_private; + + /* If the device isn't started don't handle interrupts */ + if (!bonded_eth_dev->data->dev_started) + return; + + /* verify that port_id is a valid slave of bonded port */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == port_id) { + valid_slave = 1; + break; + } + } + + if (!valid_slave) + return; + + /* Search for port in active port list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (port_id == internals->active_slaves[i]) { + active_pos = i; + break; + } + } + + rte_eth_link_get_nowait(port_id, &link); + if (link.link_status) { + if (active_pos == -1) { + /* if no active slave ports then set this port to be primary port */ + if (internals->active_slave_count == 0) { + /* If first active slave, then change link status */ + bonded_eth_dev->data->dev_link.link_status = 1; + internals->current_primary_port = port_id; + + /* Inherit eth dev link properties from first active slave */ + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + + } + internals->active_slaves[internals->active_slave_count++] = port_id; + + /* If user has defined the primary port then default to using it */ + if (internals->user_defined_primary_port && + internals->primary_port == port_id) + bond_ethdev_primary_set(internals, port_id); + + } + } else { + if (active_pos != -1) { + /* Remove from active slave list */ + for (i = active_pos; i < (internals->active_slave_count - 1); i++) + internals->active_slaves[i] = internals->active_slaves[i+1]; + + internals->active_slave_count--; + + /* No active slaves, change link status to down and reset other + * link properties */ + if (internals->active_slave_count == 0) + link_properties_reset(bonded_eth_dev); + + /* Update primary id, take first active slave from list or if none + * available set to -1 */ + if (port_id == internals->current_primary_port) { + if (internals->active_slave_count > 0) + bond_ethdev_primary_set(internals, + internals->active_slaves[0]); + else + internals->current_primary_port = internals->primary_port; + } + } + } +} + +static struct eth_dev_ops default_dev_ops = { + .dev_start = bond_ethdev_start, + .dev_stop = bond_ethdev_stop, + .dev_close = bond_ethdev_close, + .dev_configure = bond_ethdev_configure, + .dev_infos_get = bond_ethdev_info, + .rx_queue_setup = bond_ethdev_rx_queue_setup, + .tx_queue_setup = bond_ethdev_tx_queue_setup, + .rx_queue_release = bond_ethdev_rx_queue_release, + .tx_queue_release = bond_ethdev_tx_queue_release, + .link_update = bond_ethdev_link_update, + .stats_get = bond_ethdev_stats_get, + .stats_reset = bond_ethdev_stats_reset, + .promiscuous_enable = bond_ethdev_promiscuous_enable, + .promiscuous_disable = bond_ethdev_promiscuous_disable +}; + +static uint8_t +number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; ((i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL)); i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; + +} + +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct bond_dev_private *internals = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct rte_pci_id *pci_id_table = NULL; + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (private) data + */ + + if (name == NULL) { + RTE_LOG(ERR, PMD, "Invalid name specified\n"); + goto err; + } + + if (socket_id >= number_of_sockets()) { + RTE_LOG(ERR, PMD, + "%s: invalid socket id specified to create bonded device on.\n", + __func__); + goto err; + } + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci dev on socket"); + goto err; + } + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc eth_drv on socket"); + goto err; + } + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_drv on socket"); + goto err; + } + pci_id_table = rte_zmalloc_socket(name, sizeof(*pci_id_table), 0, socket_id); + if (pci_drv == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc pci_id_table on socket"); + goto err; + } + + pci_drv->id_table = pci_id_table; + + pci_drv->id_table->device_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_device_id = PCI_ANY_ID; + pci_drv->id_table->vendor_id = PCI_ANY_ID; + pci_drv->id_table->subsystem_vendor_id = PCI_ANY_ID; + + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, socket_id); + if (internals == NULL) { + RTE_LOG(ERR, PMD, "Unable to malloc internals on socket"); + goto err; + } + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) { + RTE_LOG(ERR, PMD, "Unable to allocate rte_eth_dev"); + goto err; + } + + pci_dev->numa_node = socket_id; + pci_drv->name = driver_name; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->dev_private = internals; + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + eth_dev->data->dev_link.link_status = 0; + + eth_dev->data->mac_addrs = rte_zmalloc_socket(name, ETHER_ADDR_LEN, 0, + socket_id); + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + eth_dev->dev_ops = &default_dev_ops; + eth_dev->pci_dev = pci_dev; + + eth_dev->rx_pkt_burst = bond_ethdev_rx_burst; + if (bond_ethdev_mode_set(eth_dev, mode)) { + RTE_LOG(ERR, PMD, + "%s: failed to set bonded device %d mode too %d\n", + __func__, eth_dev->data->port_id, mode); + goto err; + } + + internals->current_primary_port = 0; + internals->balance_xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + internals->user_defined_mac = 0; + internals->link_props_set = 0; + internals->slave_count = 0; + internals->active_slave_count = 0; + + memset(internals->active_slaves, 0, sizeof(internals->active_slaves)); + memset(internals->slaves, 0, sizeof(internals->slaves)); + + memset(internals->presisted_slaves_conf, 0, + sizeof(internals->presisted_slaves_conf)); + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (pci_id_table) + rte_free(pci_id_table); + if (eth_drv) + rte_free(eth_drv); + if (internals) + rte_free(internals); + return -1; +} + +static inline int +find_port_id_by_pci_addr(const struct rte_pci_addr *pci_addr) +{ + struct rte_pci_addr *eth_pci_addr; + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + + if (rte_eth_devices[i].pci_dev == NULL) + continue; + + eth_pci_addr = &(rte_eth_devices[i].pci_dev->addr); + + if (pci_addr->bus == eth_pci_addr->bus && + pci_addr->devid == eth_pci_addr->devid && + pci_addr->domain == eth_pci_addr->domain && + pci_addr->function == eth_pci_addr->function) + return i; + } + return -1; +} + +static inline int +find_port_id_by_dev_name(const char *name) +{ + unsigned i; + + for (i = 0; i < rte_eth_dev_count(); i++) { + if (rte_eth_devices[i].data == NULL) + continue; + + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return i; + } + return -1; +} + +/** + * Parses a port identifier string to a port id by pci address, then by name, + * and finally port id. + */ +static inline int +parse_port_id(const char *port_str) +{ + struct rte_pci_addr dev_addr; + int port_id; + + /* try parsing as pci address, physical devices */ + if (eal_parse_pci_DomBDF(port_str, &dev_addr) == 0) { + port_id = find_port_id_by_pci_addr(&dev_addr); + if (port_id < 0) + return -1; + } else { + /* try parsing as device name, virtual devices */ + port_id = find_port_id_by_dev_name(port_str); + if (port_id < 0) { + char *end; + errno = 0; + + /* try parsing as port id */ + port_id = strtol(port_str, &end, 10); + if (*end != 0 || errno != 0) + return -1; + } + } + + if (port_id < 0 || port_id > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, PMD, "Invalid slave port value (%s) specified.\n", + port_str); + return -1; + } + + return port_id; +} +static int +bond_ethdev_parse_slave_port_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + struct bond_ethdev_slave_ports *slave_ports; + + if (value == NULL || extra_args == NULL) + return -1; + + slave_ports = extra_args; + + if (strcmp(key, PMD_BOND_SLAVE_PORT_KVARG) == 0) { + int port_id = parse_port_id(value); + if (port_id < 0) + return -1; + else + slave_ports->slaves[slave_ports->slave_count++] = + (uint8_t)port_id; + } + return 0; +} + +static int +bond_ethdev_parse_slave_mode_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *mode; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + mode = extra_args; + + errno = 0; + *mode = strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + switch (*mode) { + case BONDING_MODE_ROUND_ROBIN: + case BONDING_MODE_ACTIVE_BACKUP: + case BONDING_MODE_BALANCE: + case BONDING_MODE_BROADCAST: + return 0; + default: + return -1; + } +} + +static int +bond_ethdev_parse_socket_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int socket_id; + char *endptr; + + if (value == NULL || extra_args == NULL) + return -1; + + errno = 0; + socket_id = (uint8_t)strtol(value, &endptr, 10); + if (*endptr != 0 || errno != 0) + return -1; + + /* validate mode value */ + if (socket_id >= 0 && socket_id < number_of_sockets()) { + *(uint8_t *)extra_args = (uint8_t)socket_id; + return 0; + } + return -1; +} + +static int +bond_ethdev_parse_primary_slave_port_id_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + int primary_slave_port_id; + + if (value == NULL || extra_args == NULL) + return -1; + + primary_slave_port_id = parse_port_id(value); + if (primary_slave_port_id < 0) + return -1; + + *(uint8_t *)extra_args = (uint8_t)primary_slave_port_id; + + return 0; +} + +static int +bond_ethdev_parse_balance_xmit_policy_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + uint8_t *xmit_policy; + + if (value == NULL || extra_args == NULL) + return -1; + + xmit_policy = extra_args; + + if (strcmp(PMD_BOND_XMIT_POLICY_LAYER2_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER2; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER23_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER23; + else if (strcmp(PMD_BOND_XMIT_POLICY_LAYER34_KVARG, value) == 0) + *xmit_policy = BALANCE_XMIT_POLICY_LAYER34; + else + return -1; + + return 0; +} + +static int +bond_ethdev_parse_bond_mac_addr_kvarg(const char *key __rte_unused, + const char *value, void *extra_args) +{ + if (value == NULL || extra_args == NULL) + return -1; + + /* Parse MAC */ + return cmdline_parse_etheraddr(NULL, value, extra_args); +} + + +static int +rte_pmd_bond_init(const char *name, const char *params) +{ + struct rte_kvargs *kvlist; + uint8_t bonding_mode, socket_id; + int arg_count, port_id; + + RTE_LOG(INFO, EAL, "Initializing pmd_bond for %s\n", name); + + kvlist = rte_kvargs_parse(params, pmd_bond_init_valid_arguments); + if (kvlist == NULL) + return -1; + + /* Parse link bonding mode */ + if (rte_kvargs_count(kvlist, PMD_BOND_MODE_KVARG) == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_MODE_KVARG, + &bond_ethdev_parse_slave_mode_kvarg, &bonding_mode) != 0) { + RTE_LOG(ERR, EAL, "Invalid mode for bonded device %s\n", name); + return -1; + } + } else { + RTE_LOG(ERR, EAL, + "Mode must be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse socket id to create bonding device on */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_SOCKET_ID_KVARG); + if (arg_count == 1) { + if (rte_kvargs_process(kvlist, PMD_BOND_SOCKET_ID_KVARG, + &bond_ethdev_parse_socket_id_kvarg, &socket_id) != 0) { + RTE_LOG(ERR, EAL, + "Invalid socket Id specified for bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Socket Id can be specified only once for bonded device %s\n", + name); + return -1; + } else { + socket_id = rte_socket_id(); + } + + /* Create link bonding eth device */ + port_id = rte_eth_bond_create(name, bonding_mode, socket_id); + if (port_id < 0) { + RTE_LOG(ERR, EAL, + "Failed to create socket %s in mode %u on socket %u.\n", + name, bonding_mode, socket_id); + return -1; + } + + RTE_LOG(INFO, EAL, + "Create bonded device %s on port %d in mode %u on socket %u.\n", + name, port_id, bonding_mode, socket_id); + + /* Parse MAC address for bonded device */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_MAC_ADDR_KVARG); + if (arg_count == 1) { + struct ether_addr bond_mac; + + if (rte_kvargs_process(kvlist, PMD_BOND_MAC_ADDR_KVARG, + &bond_ethdev_parse_bond_mac_addr_kvarg, &bond_mac) < 0) { + RTE_LOG(INFO, EAL, "Invalid mac address for bonded device %s\n", + name); + return -1; + } + + /* Set MAC address */ + if (rte_eth_bond_mac_address_set(port_id, &bond_mac) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set mac address on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "MAC address can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/set balance mode transmit policy */ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_XMIT_POLICY_KVARG); + if (arg_count == 1) { + uint8_t xmit_policy; + + if (rte_kvargs_process(kvlist, PMD_BOND_XMIT_POLICY_KVARG, + &bond_ethdev_parse_balance_xmit_policy_kvarg, &xmit_policy) != + 0) { + RTE_LOG(INFO, EAL, + "Invalid xmit policy specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_xmit_policy_set(port_id, xmit_policy) != 0) { + RTE_LOG(ERR, EAL, + "Failed to set balance xmit policy on bonded device %s\n", + name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(ERR, EAL, + "Transmit policy can be specified only once for bonded device %s\n", + name); + return -1; + } + + /* Parse/add slave ports to bonded device */ + if (rte_kvargs_count(kvlist, PMD_BOND_SLAVE_PORT_KVARG) > 0) { + struct bond_ethdev_slave_ports slave_ports; + unsigned i; + + memset(&slave_ports, 0, sizeof(slave_ports)); + + if (rte_kvargs_process(kvlist, PMD_BOND_SLAVE_PORT_KVARG, + &bond_ethdev_parse_slave_port_kvarg, &slave_ports) != 0) { + RTE_LOG(ERR, EAL, + "Failed to parse slave ports for bonded device %s\n", + name); + return -1; + } + + for (i = 0; i < slave_ports.slave_count; i++) { + if (rte_eth_bond_slave_add(port_id, slave_ports.slaves[i]) != 0) { + RTE_LOG(ERR, EAL, + "Failed to add port %d as slave to bonded device %s\n", + slave_ports.slaves[i], name); + } + } + + } else { + RTE_LOG(INFO, EAL, "No slaves specified for bonded device %s\n", name); + return -1; + } + + /* Parse/set primary slave port id*/ + arg_count = rte_kvargs_count(kvlist, PMD_BOND_PRIMARY_SLAVE_KVARG); + if (arg_count == 1) { + uint8_t primary_slave_port_id; + + if (rte_kvargs_process(kvlist, + PMD_BOND_PRIMARY_SLAVE_KVARG, + &bond_ethdev_parse_primary_slave_port_id_kvarg, + &primary_slave_port_id) < 0) { + RTE_LOG(INFO, EAL, + "Invalid primary slave port id specified for bonded device %s\n", + name); + return -1; + } + + /* Set balance mode transmit policy*/ + if (rte_eth_bond_primary_set(port_id, (uint8_t)primary_slave_port_id) + != 0) { + RTE_LOG(ERR, EAL, + "Failed to set primary slave port %d on bonded device %s\n", + primary_slave_port_id, name); + return -1; + } + } else if (arg_count > 1) { + RTE_LOG(INFO, EAL, + "Primary slave can be specified only once for bonded device %s\n", + name); + return -1; + } + + return 0; +} + +static struct rte_driver rte_pmd_bond_drv = { + .name = PMD_BOND_KVARG, + .type = PMD_BDEV, + .init = rte_pmd_bond_init, +}; + +PMD_REGISTER_DRIVER(rte_pmd_bond_drv); + +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev; + struct bond_dev_private *internals; + struct bond_dev_private *temp_internals; + struct rte_eth_link link_props; + + int i, j; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_add; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_add; + + /* + * Verify that new slave device is not already a slave of another bonded + * device */ + for (i = rte_eth_dev_count()-1; i >= 0; i--) { + if (valid_bonded_ethdev(&rte_eth_devices[i]) == 0) { + temp_internals = rte_eth_devices[i].data->dev_private; + for (j = 0; j < temp_internals->slave_count; j++) { + /* Device already a slave of a bonded device */ + if (temp_internals->slaves[j] == slave_port_id) + goto err_add; + } + } + } + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + slave_eth_dev = &rte_eth_devices[slave_port_id]; + + if (internals->slave_count > 0) { + /* Check that new slave device is the same type as the other slaves + * and not repetitive */ + for (i = 0; i < internals->slave_count; i++) { + if (slave_eth_dev->pci_dev->driver->id_table->device_id != + rte_eth_devices[internals->slaves[i]].pci_dev->driver->id_table->device_id || + internals->slaves[i] == slave_port_id) + goto err_add; + } + } + + /* Add slave details to bonded device */ + internals->slaves[internals->slave_count] = slave_port_id; + + slave_config_store(internals, slave_eth_dev); + + if (internals->slave_count < 1) { + /* if MAC is not user defined then use MAC of first slave add to bonded + * device */ + if (!internals->user_defined_mac) + mac_address_set(bonded_eth_dev, slave_eth_dev->data->mac_addrs); + + /* Inherit eth dev link properties from first slave */ + link_properties_set(bonded_eth_dev, &(slave_eth_dev->data->dev_link)); + + /* Make primary slave */ + internals->primary_port = slave_port_id; + } else { + /* Check slave link properties are supported if props are set, + * all slaves must be the same */ + if (internals->link_props_set) { + if (link_properties_valid(&(bonded_eth_dev->data->dev_link), + &(slave_eth_dev->data->dev_link))) { + RTE_LOG(ERR, PMD, + "%s: Slave port %d link speed/duplex not supported\n", + __func__, slave_port_id); + goto err_add; + } + } else { + link_properties_set(bonded_eth_dev, + &(slave_eth_dev->data->dev_link)); + } + } + + internals->slave_count++; + + /* Update all slave devices MACs*/ + mac_address_slaves_update(bonded_eth_dev); + + if (bonded_eth_dev->data->dev_started) { + if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) { + RTE_LOG(ERR, PMD, "rte_bond_slaves_configure: port=%d\n", + slave_port_id); + goto err_add; + } + } + + /* Register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_register(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, &bonded_eth_dev->data->port_id); + + /* If bonded device is started then we can add the slave to our active + * slave array */ + if (bonded_eth_dev->data->dev_started) { + rte_eth_link_get_nowait(slave_port_id, &link_props); + + if (link_props.link_status == 1) { + internals->active_slaves[internals->active_slave_count++] = + slave_port_id; + } + } + + return 0; + +err_add: + RTE_LOG(ERR, PMD, "Failed to add port %d as slave\n", slave_port_id); + return -1; + +} + +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + struct slave_conf *slave_conf; + + int i; + int pos = -1; + + /* Verify that port id's are valid bonded and slave ports */ + if (valid_bonded_port_id(bonded_port_id) != 0) + goto err_del; + + if (valid_slave_port_id(slave_port_id) != 0) + goto err_del; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + + /* first remove from active slave list */ + for (i = 0; i < internals->active_slave_count; i++) { + if (internals->active_slaves[i] == slave_port_id) + pos = i; + + /* shift active slaves up active array list */ + if (pos >= 0 && i < (internals->active_slave_count - 1)) + internals->active_slaves[i] = internals->active_slaves[i+1]; + } + + if (pos >= 0) + internals->active_slave_count--; + + + pos = -1; + /* now remove from slave list */ + for (i = 0; i < internals->slave_count; i++) { + if (internals->slaves[i] == slave_port_id) + pos = i; + + /* shift slaves up list */ + if (pos >= 0 && i < internals->slave_count) + internals->slaves[i] = internals->slaves[i+1]; + } + + if (pos < 0) + goto err_del; + + /* Un-register link status change callback with bonded device pointer as + * argument*/ + rte_eth_dev_callback_unregister(slave_port_id, RTE_ETH_EVENT_INTR_LSC, + bond_ethdev_lsc_event_callback, + &rte_eth_devices[bonded_port_id].data->port_id); + + /* Restore original MAC address of slave device */ + slave_conf = slave_config_get(internals, slave_port_id); + + mac_address_set(&rte_eth_devices[slave_port_id], &(slave_conf->mac_addr)); + + slave_config_clear(internals, &rte_eth_devices[slave_port_id]); + + internals->slave_count--; + + /* first slave in the active list will be the primary by default, + * otherwise use first device in list */ + if (internals->current_primary_port == slave_port_id) { + if (internals->active_slave_count > 0) + internals->current_primary_port = internals->active_slaves[0]; + else if (internals->slave_count > 0) + internals->current_primary_port = internals->slaves[0]; + else + internals->primary_port = 0; + } + + if (internals->active_slave_count < 1) { + /* reset device link properties as no slaves are active */ + link_properties_reset(&rte_eth_devices[bonded_port_id]); + + /* if no slaves are any longer attached to bonded device and MAC is not + * user defined then clear MAC of bonded device as it will be reset + * when a new slave is added */ + if (internals->slave_count < 1 && !internals->user_defined_mac) + memset(rte_eth_devices[bonded_port_id].data->mac_addrs, 0, + sizeof(*(rte_eth_devices[bonded_port_id].data->mac_addrs))); + } + + return 0; + +err_del: + RTE_LOG(ERR, PMD, + "Cannot remove slave device (not present in bonded device)\n"); + return -1; + +} + +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode) +{ + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + return bond_ethdev_mode_set(&rte_eth_devices[bonded_port_id], mode); +} + + +int +rte_eth_bond_mode_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->mode; +} + + +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (valid_slave_port_id(slave_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + internals->user_defined_primary_port = 1; + internals->primary_port = slave_port_id; + + bond_ethdev_primary_set(internals, slave_port_id); + + return 0; +} + +int +rte_eth_bond_primary_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count < 1) + return -1; + + return internals->current_primary_port; +} +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->slave_count > len) + return -1; + + memcpy(slaves, internals->slaves, internals->slave_count); + + return internals->slave_count; + +} + +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + if (slaves == NULL) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + if (internals->active_slave_count > len) + return -1; + + memcpy(slaves, internals->active_slaves, internals->active_slave_count); + + return internals->active_slave_count; +} + + +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, mac_addr)) + return -1; + + internals->user_defined_mac = 1; + + /* Update all slave devices MACs*/ + if (internals->slave_count > 0) + return mac_address_slaves_update(bonded_eth_dev); + + return 0; +} + + +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id) +{ + struct rte_eth_dev *bonded_eth_dev; + struct bond_dev_private *internals; + + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + bonded_eth_dev = &rte_eth_devices[bonded_port_id]; + internals = bonded_eth_dev->data->dev_private; + + internals->user_defined_mac = 0; + + if (internals->slave_count > 0) { + struct slave_conf *conf; + conf = slave_config_get(internals, internals->primary_port); + + /* Set MAC Address of Bonded Device */ + if (mac_address_set(bonded_eth_dev, &conf->mac_addr) != 0) + return -1; + + /* Update all slave devices MAC addresses */ + return mac_address_slaves_update(bonded_eth_dev); + } + /* No need to update anything as no slaves present */ + return 0; +} + +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + switch (policy) { + case BALANCE_XMIT_POLICY_LAYER2: + case BALANCE_XMIT_POLICY_LAYER23: + case BALANCE_XMIT_POLICY_LAYER34: + internals->balance_xmit_policy = policy; + break; + + default: + return -1; + } + return 0; +} + + +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id) +{ + struct bond_dev_private *internals; + + if (valid_bonded_port_id(bonded_port_id) != 0) + return -1; + + internals = rte_eth_devices[bonded_port_id].data->dev_private; + + return internals->balance_xmit_policy; +} + diff --git a/lib/librte_pmd_bond/rte_eth_bond.h b/lib/librte_pmd_bond/rte_eth_bond.h new file mode 100644 index 0000000..95a4fa9 --- /dev/null +++ b/lib/librte_pmd_bond/rte_eth_bond.h @@ -0,0 +1,255 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RTE_ETH_BOND_H_ +#define _RTE_ETH_BOND_H_ + +/** + * @file rte_bond.h + * + * RTE Link Bonding Ethernet Device + * Link Bonding for 1GbE and 10GbE ports to allow the aggregation of multiple + * (slave) NICs into a single logical interface. The bonded device processes + * these interfaces based on the mode of operation specified and supported. + * This implementation supports 4 modes of operation round robin, active backup + * balance and broadcast. Providing redundant links, fault tolerance and/or + * load balancing of network ports + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +/* Supported modes of operation of link bonding library */ + +#define BONDING_MODE_ROUND_ROBIN (0) +/**< Round Robin (Mode 0). + * In this mode all transmitted packets will be balanced equally across all + * active slaves of the bonded in a round robin fashion. */ +#define BONDING_MODE_ACTIVE_BACKUP (1) +/**< Active Backup (Mode 1). + * In this mode all packets transmitted will be transmitted on the primary + * slave until such point as the primary slave is no longer available and then + * transmitted packets will be sent on the next available slaves. The primary + * slave can be defined by the user but defaults to the first active slave + * available if not specified. */ +#define BONDING_MODE_BALANCE (2) +/**< Balance (Mode 2). + * In this mode all packets transmitted will be balanced across the available + * slaves using one of three available transmit policies - l2, l2+3 or l3+4. + * See BALANCE_XMIT_POLICY macros definitions for further details on transmit + * policies. */ +#define BONDING_MODE_BROADCAST (3) +/**< Broadcast (Mode 3). + * In this mode all transmitted packets will be transmitted on all available + * active slaves of the bonded. */ + +/* Balance Mode Transmit Policies */ +#define BALANCE_XMIT_POLICY_LAYER2 (0) +/**< Layer 2 (Ethernet MAC) */ +#define BALANCE_XMIT_POLICY_LAYER23 (1) +/**< Layer 2+3 (Ethernet MAC + IP Addresses) transmit load balancing */ +#define BALANCE_XMIT_POLICY_LAYER34 (2) +/**< Layer 3+4 (IP Addresses + UDP Ports) transmit load balancing */ + +/** + * Create a bonded rte_eth_dev device + * + * @param name Name of new link bonding device. + * @param mode Mode to initialize bonding device in. + * @param socket_id Socket Id on which to allocate eth_dev resources. + * + * @return + * Port Id of created rte_eth_dev on success, negative value otherwise + */ +int +rte_eth_bond_create(const char *name, uint8_t mode, uint8_t socket_id); + +/** + * Add a rte_eth_dev device as a slave to the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_add(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Remove a slave rte_eth_dev device from the bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_slave_remove(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Set link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param mode Bonding mode to set + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mode_set(uint8_t bonded_port_id, uint8_t mode); + +/** + * Get link bonding mode of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * link bonding mode on success, negative value otherwise + */ +int +rte_eth_bond_mode_get(uint8_t bonded_port_id); + +/** + * Set slave rte_eth_dev as primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * @param slave_port_id Port ID of slave device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_primary_set(uint8_t bonded_port_id, uint8_t slave_port_id); + +/** + * Get primary slave of bonded device + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Port Id of primary slave on success, -1 on failure + */ +int +rte_eth_bond_primary_get(uint8_t bonded_port_id); + +/** + * Populate an array with list of the slaves port id's of the bonded device + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], uint8_t len); + +/** + * Populate an array with list of the active slaves port id's of the bonded + * device. + * + * @param bonded_port_id Port ID of bonded eth_dev to interrogate + * @param slaves Array to be populated with the current active slaves + * @param len Length of slaves array + * + * @return + * Number of active slaves associated with bonded device on success, + * negative value otherwise + */ +int +rte_eth_bond_active_slaves_get(uint8_t bonded_port_id, uint8_t slaves[], + uint8_t len); + +/** + * Set explicit MAC address to use on bonded device and it's slaves. + * + * @param bonded_port_id Port ID of bonded device. + * @param mac_addr MAC Address to use on bonded device overriding + * slaves MAC addresses + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_set(uint8_t bonded_port_id, + struct ether_addr *mac_addr); + +/** + * Reset bonded device to use MAC from primary slave on bonded device and it's + * slaves. + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * 0 on success, negative value otherwise + */ +int +rte_eth_bond_mac_address_reset(uint8_t bonded_port_id); + +/** + * Set the transmit policy for bonded device to use when it is operating in + * balance mode, this parameter is otherwise ignored in other modes of + * operation. + * + * @param bonded_port_id Port ID of bonded device. + * @param policy Balance mode transmission policy. + * + * @return + * 0 on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_set(uint8_t bonded_port_id, uint8_t policy); + +/** + * Get the transmit policy set on bonded device for balance mode operation + * + * @param bonded_port_id Port ID of bonded device. + * + * @return + * Balance transmit policy on success, negative value otherwise. + */ +int +rte_eth_bond_xmit_policy_get(uint8_t bonded_port_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 914c568..66de6a8 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -181,8 +181,13 @@ ifeq ($(CONFIG_RTE_LIBRTE_PMD_PCAP),y) LDLIBS += -lrte_pmd_pcap -lpcap endif +ifeq ($(CONFIG_RTE_LIBRTE_PMD_BOND),y) +LDLIBS += -lrte_pmd_bond endif +endif + + LDLIBS += $(EXECENV_LDLIBS) LDLIBS += --end-group -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v4 2/6] Support for unique interface naming of pmds 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (7 preceding siblings ...) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 1/6] Link Bonding Library (lib/librte_pmd_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, la Mode 3 - Broadcast Declan Doherty @ 2014-06-16 11:18 ` Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 3/6] EAL support for link bonding device initialization Declan Doherty ` (3 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-16 11:18 UTC (permalink / raw) To: dev Adding support to rte_eth_dev_data structure to support unique name identifier for ethdevs to support adding slave ethdevs (specifically virtual devices which have no public unique identifier) to a link bonding device. This changes the API rte_eth_dev_allocate() to require a const char *name when allocating a ethdev, which also verifies that the name is unique and hasn’t been already used by an existed allocated rte_eth_dev. Also contains updates to virtual pmd’s to now call the API with a name parameter. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_ether/rte_ethdev.c | 33 +++++++++++++++++++++++++++-- lib/librte_ether/rte_ethdev.h | 7 +++++- lib/librte_pmd_pcap/rte_eth_pcap.c | 22 ++++++++++---------- lib/librte_pmd_ring/rte_eth_ring.c | 32 +++++++++++++++------------- lib/librte_pmd_ring/rte_eth_ring.h | 3 +- lib/librte_pmd_xenvirt/rte_eth_xenvirt.c | 2 +- 6 files changed, 67 insertions(+), 32 deletions(-) diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c index 8011b8b..e5373fe 100644 --- a/lib/librte_ether/rte_ethdev.c +++ b/lib/librte_ether/rte_ethdev.c @@ -64,6 +64,7 @@ #include <rte_mbuf.h> #include <rte_errno.h> #include <rte_spinlock.h> +#include <rte_string_fns.h> #include "rte_ether.h" #include "rte_ethdev.h" @@ -152,8 +153,21 @@ rte_eth_dev_data_alloc(void) RTE_MAX_ETHPORTS * sizeof(*rte_eth_dev_data)); } +static int +rte_eth_dev_name_unique(const char *name) +{ + unsigned i; + + for (i = 0; i < nb_ports; i++) { + if (strcmp(rte_eth_devices[i].data->name, name) == 0) + return -1; + } + + return 0; +} + struct rte_eth_dev * -rte_eth_dev_allocate(void) +rte_eth_dev_allocate(const char *name) { struct rte_eth_dev *eth_dev; @@ -165,23 +179,36 @@ rte_eth_dev_allocate(void) if (rte_eth_dev_data == NULL) rte_eth_dev_data_alloc(); + if (rte_eth_dev_name_unique(name)) { + PMD_DEBUG_TRACE("Ethernet Device with name %s already allocated!\n"); + return NULL; + } + eth_dev = &rte_eth_devices[nb_ports]; eth_dev->data = &rte_eth_dev_data[nb_ports]; + rte_snprintf(eth_dev->data->name, sizeof(eth_dev->data->name), + "%s", name); eth_dev->data->port_id = nb_ports++; return eth_dev; } static int rte_eth_dev_init(struct rte_pci_driver *pci_drv, - struct rte_pci_device *pci_dev) + struct rte_pci_device *pci_dev) { struct eth_driver *eth_drv; struct rte_eth_dev *eth_dev; + char ethdev_name[RTE_ETH_NAME_MAX_LEN]; + int diag; eth_drv = (struct eth_driver *)pci_drv; - eth_dev = rte_eth_dev_allocate(); + /* Create unique ethdev name from pci address */ + rte_snprintf(ethdev_name, RTE_ETH_NAME_MAX_LEN, "%d:%d.%d", + pci_dev->addr.bus, pci_dev->addr.devid, pci_dev->addr.function); + + eth_dev = rte_eth_dev_allocate(ethdev_name); if (eth_dev == NULL) return -ENOMEM; diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h index 67eda50..27ed0ab 100644 --- a/lib/librte_ether/rte_ethdev.h +++ b/lib/librte_ether/rte_ethdev.h @@ -1233,6 +1233,8 @@ struct rte_eth_dev_sriov { }; #define RTE_ETH_DEV_SRIOV(dev) ((dev)->data->sriov) +#define RTE_ETH_NAME_MAX_LEN (32) + /** * @internal * The data part, with no function pointers, associated with each ethernet device. @@ -1241,6 +1243,8 @@ struct rte_eth_dev_sriov { * processes in a multi-process configuration. */ struct rte_eth_dev_data { + char name[RTE_ETH_NAME_MAX_LEN]; /**< Unique identifier name */ + void **rx_queues; /**< Array of pointers to RX queues. */ void **tx_queues; /**< Array of pointers to TX queues. */ uint16_t nb_rx_queues; /**< Number of RX queues. */ @@ -1293,10 +1297,11 @@ extern uint8_t rte_eth_dev_count(void); * Allocates a new ethdev slot for an ethernet device and returns the pointer * to that slot for the driver to use. * + * @param name Unique identifier name for each Ethernet device * @return * - Slot in the rte_dev_devices array for a new device; */ -struct rte_eth_dev *rte_eth_dev_allocate(void); +struct rte_eth_dev *rte_eth_dev_allocate(const char *name); struct eth_driver; /** diff --git a/lib/librte_pmd_pcap/rte_eth_pcap.c b/lib/librte_pmd_pcap/rte_eth_pcap.c index b3dbbda..12b7e0c 100644 --- a/lib/librte_pmd_pcap/rte_eth_pcap.c +++ b/lib/librte_pmd_pcap/rte_eth_pcap.c @@ -534,7 +534,7 @@ open_tx_iface(const char *key __rte_unused, const char *value, void *extra_args) static int -rte_pmd_init_internals(const unsigned nb_rx_queues, +rte_pmd_init_internals(const char *name, const unsigned nb_rx_queues, const unsigned nb_tx_queues, const unsigned numa_node, struct pmd_internals **internals, @@ -558,20 +558,20 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - *internals = rte_zmalloc_socket(NULL, sizeof(**internals), 0, numa_node); + *internals = rte_zmalloc_socket(name, sizeof(**internals), 0, numa_node); if (*internals == NULL) goto error; /* reserve an ethdev entry */ - *eth_dev = rte_eth_dev_allocate(); + *eth_dev = rte_eth_dev_allocate(name); if (*eth_dev == NULL) goto error; @@ -617,7 +617,7 @@ rte_pmd_init_internals(const unsigned nb_rx_queues, } static int -rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], +rte_eth_from_pcaps_n_dumpers(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_dumper_t * const tx_queues[], const unsigned nb_tx_queues, @@ -634,7 +634,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -652,7 +652,7 @@ rte_eth_from_pcaps_n_dumpers(pcap_t * const rx_queues[], } static int -rte_eth_from_pcaps(pcap_t * const rx_queues[], +rte_eth_from_pcaps(const char *name, pcap_t * const rx_queues[], const unsigned nb_rx_queues, pcap_t * const tx_queues[], const unsigned nb_tx_queues, @@ -669,7 +669,7 @@ rte_eth_from_pcaps(pcap_t * const rx_queues[], if (tx_queues == NULL && nb_tx_queues > 0) return -1; - if (rte_pmd_init_internals(nb_rx_queues, nb_tx_queues, numa_node, + if (rte_pmd_init_internals(name, nb_rx_queues, nb_tx_queues, numa_node, &internals, ð_dev, kvlist) < 0) return -1; @@ -760,10 +760,10 @@ rte_pmd_pcap_devinit(const char *name, const char *params) return -1; if (using_dumpers) - return rte_eth_from_pcaps_n_dumpers(pcaps.pcaps, pcaps.num_of_rx, + return rte_eth_from_pcaps_n_dumpers(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.dumpers, dumpers.num_of_tx, numa_node, kvlist); - return rte_eth_from_pcaps(pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, + return rte_eth_from_pcaps(name, pcaps.pcaps, pcaps.num_of_rx, dumpers.pcaps, dumpers.num_of_tx, numa_node, kvlist); } diff --git a/lib/librte_pmd_ring/rte_eth_ring.c b/lib/librte_pmd_ring/rte_eth_ring.c index 10d4e24..f9174f1 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.c +++ b/lib/librte_pmd_ring/rte_eth_ring.c @@ -219,7 +219,7 @@ static struct eth_dev_ops ops = { }; int -rte_eth_from_rings(struct rte_ring *const rx_queues[], +rte_eth_from_rings(const char *name, struct rte_ring *const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, @@ -243,20 +243,20 @@ rte_eth_from_rings(struct rte_ring *const rx_queues[], /* now do all data allocation - for eth_dev structure, dummy pci driver * and internal (private) data */ - data = rte_zmalloc_socket(NULL, sizeof(*data), 0, numa_node); + data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node); if (data == NULL) goto error; - pci_dev = rte_zmalloc_socket(NULL, sizeof(*pci_dev), 0, numa_node); + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, numa_node); if (pci_dev == NULL) goto error; - internals = rte_zmalloc_socket(NULL, sizeof(*internals), 0, numa_node); + internals = rte_zmalloc_socket(name, sizeof(*internals), 0, numa_node); if (internals == NULL) goto error; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto error; @@ -335,7 +335,7 @@ eth_dev_ring_create(const char *name, const unsigned numa_node, return -1; } - if (rte_eth_from_rings(rxtx, num_rings, rxtx, num_rings, numa_node)) + if (rte_eth_from_rings(name, rxtx, num_rings, rxtx, num_rings, numa_node)) return -1; return 0; @@ -352,29 +352,31 @@ eth_dev_ring_pair_create(const char *name, const unsigned numa_node, struct rte_ring *rx[RTE_PMD_RING_MAX_RX_RINGS]; struct rte_ring *tx[RTE_PMD_RING_MAX_TX_RINGS]; unsigned i; - char rng_name[RTE_RING_NAMESIZE]; + char rx_rng_name[RTE_RING_NAMESIZE]; + char tx_rng_name[RTE_RING_NAMESIZE]; unsigned num_rings = RTE_MIN(RTE_PMD_RING_MAX_RX_RINGS, RTE_PMD_RING_MAX_TX_RINGS); for (i = 0; i < num_rings; i++) { - rte_snprintf(rng_name, sizeof(rng_name), "ETH_RX%u_%s", i, name); + rte_snprintf(rx_rng_name, sizeof(rx_rng_name), "ETH_RX%u_%s", i, name); rx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(rx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ) : - rte_ring_lookup(rng_name); + rte_ring_lookup(rx_rng_name); if (rx[i] == NULL) return -1; - rte_snprintf(rng_name, sizeof(rng_name), "ETH_TX%u_%s", i, name); + rte_snprintf(tx_rng_name, sizeof(tx_rng_name), "ETH_TX%u_%s", i, name); tx[i] = (action == DEV_CREATE) ? - rte_ring_create(rng_name, 1024, numa_node, + rte_ring_create(tx_rng_name, 1024, numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ): - rte_ring_lookup(rng_name); + rte_ring_lookup(tx_rng_name); if (tx[i] == NULL) return -1; } - if (rte_eth_from_rings(rx, num_rings, tx, num_rings, numa_node) || - rte_eth_from_rings(tx, num_rings, rx, num_rings, numa_node) ) + if (rte_eth_from_rings(rx_rng_name, rx, num_rings, tx, num_rings, + numa_node) || rte_eth_from_rings(tx_rng_name, tx, num_rings, rx, + num_rings, numa_node)) return -1; return 0; diff --git a/lib/librte_pmd_ring/rte_eth_ring.h b/lib/librte_pmd_ring/rte_eth_ring.h index ef29344..e6ae19e 100644 --- a/lib/librte_pmd_ring/rte_eth_ring.h +++ b/lib/librte_pmd_ring/rte_eth_ring.h @@ -40,7 +40,8 @@ extern "C" { #include <rte_ring.h> -int rte_eth_from_rings(struct rte_ring * const rx_queues[], +int rte_eth_from_rings(const char *name, + struct rte_ring * const rx_queues[], const unsigned nb_rx_queues, struct rte_ring *const tx_queues[], const unsigned nb_tx_queues, diff --git a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c index 7c4d3fe..a0b4bdd 100644 --- a/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c +++ b/lib/librte_pmd_xenvirt/rte_eth_xenvirt.c @@ -647,7 +647,7 @@ eth_dev_xenvirt_create(const char *name, const char *params, goto err; /* reserve an ethdev entry */ - eth_dev = rte_eth_dev_allocate(); + eth_dev = rte_eth_dev_allocate(name); if (eth_dev == NULL) goto err; -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v4 3/6] EAL support for link bonding device initialization 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (8 preceding siblings ...) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 2/6] Support for unique interface naming of pmds Declan Doherty @ 2014-06-16 11:18 ` Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 4/6] Link bonding Unit Tests Declan Doherty ` (2 subsequent siblings) 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-16 11:18 UTC (permalink / raw) To: dev Updating functionality in EAL to support adding link bonding devices via –vdev option. Link bonding devices will be initialized after all physical devices have been probed and initialized. Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- lib/librte_eal/common/eal_common_dev.c | 66 +++++++++++++++++++++++++-- lib/librte_eal/common/eal_common_pci.c | 6 +++ lib/librte_eal/common/include/eal_private.h | 7 +++ lib/librte_eal/common/include/rte_dev.h | 1 + 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c index eae5656..b50c908 100644 --- a/lib/librte_eal/common/eal_common_dev.c +++ b/lib/librte_eal/common/eal_common_dev.c @@ -75,14 +75,28 @@ rte_eal_dev_init(void) /* call the init function for each virtual device */ TAILQ_FOREACH(devargs, &devargs_list, next) { + uint8_t bdev = 0; if (devargs->type != RTE_DEVTYPE_VIRTUAL) continue; TAILQ_FOREACH(driver, &dev_driver_list, next) { - if (driver->type != PMD_VDEV) + /* RTE_DEVTYPE_VIRTUAL can only be a virtual or bonded device*/ + if (driver->type != PMD_VDEV && driver->type != PMD_BDEV) continue; + /* + * Bonded devices are not initialize here, we do it later in + * rte_eal_bonded_dev_init() after all physical devices have been + * probed and initialized + */ + if (driver->type == PMD_BDEV && + !strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + bdev = 1; + break; + } + /* search a driver prefix in virtual device name */ if (!strncmp(driver->name, devargs->virtual.drv_name, strlen(driver->name))) { @@ -92,9 +106,9 @@ rte_eal_dev_init(void) } } - if (driver == NULL) { - rte_panic("no driver found for %s\n", - devargs->virtual.drv_name); + if (driver == NULL && !bdev) { + rte_panic("no driver found for %s and is not a bonded vdev %d\n", + devargs->virtual.drv_name, bdev); } } @@ -107,3 +121,47 @@ rte_eal_dev_init(void) } return 0; } + +#ifdef RTE_LIBRTE_PMD_BOND +int +rte_eal_bonded_dev_init(void) +{ + struct rte_devargs *devargs; + struct rte_driver *driver; + + TAILQ_FOREACH(devargs, &devargs_list, next) { + int vdev = 0; + + if (devargs->type != RTE_DEVTYPE_VIRTUAL) + continue; + + TAILQ_FOREACH(driver, &dev_driver_list, next) { + if (driver->type != PMD_VDEV && driver->type != PMD_BDEV) + continue; + + /* Virtual devices have already been initialized so we skip them + * here*/ + if (driver->type == PMD_VDEV && + !strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + vdev = 1; + break; + } + + /* search a driver prefix in bonded device name */ + if (!strncmp(driver->name, devargs->virtual.drv_name, + strlen(driver->name))) { + driver->init(devargs->virtual.drv_name, devargs->args); + break; + } + } + + if (driver == NULL && !vdev) { + rte_panic("no driver found for %s\n", + devargs->virtual.drv_name); + } + } + return 0; +} +#endif + diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index 4d877ea..9b584f5 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -166,7 +166,13 @@ rte_eal_pci_probe(void) dev->addr.devid, dev->addr.function); } +#ifdef RTE_LIBRTE_PMD_BOND + /* After all physical PCI devices have been probed and initialized then we + * initialize the bonded devices */ + return rte_eal_bonded_dev_init(); +#else return 0; +#endif } /* dump one device */ diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 232fcec..f6081bb 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -203,4 +203,11 @@ int rte_eal_alarm_init(void); */ int rte_eal_dev_init(void); +#ifdef RTE_LIBRTE_PMD_BOND +/** + * Initialize the bonded devices + */ +int rte_eal_bonded_dev_init(void); +#endif + #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h index f7e3a10..f0a780a 100644 --- a/lib/librte_eal/common/include/rte_dev.h +++ b/lib/librte_eal/common/include/rte_dev.h @@ -62,6 +62,7 @@ typedef int (rte_dev_init_t)(const char *name, const char *args); enum pmd_type { PMD_VDEV = 0, PMD_PDEV = 1, + PMD_BDEV = 2, /**< Poll Mode Driver Bonded Device*/ }; /** -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v4 4/6] Link bonding Unit Tests 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (9 preceding siblings ...) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 3/6] EAL support for link bonding device initialization Declan Doherty @ 2014-06-16 11:18 ` Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 5/6] testpmd link bonding additions Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 6/6] Link Bonding Library doxygen additions Declan Doherty 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-16 11:18 UTC (permalink / raw) To: dev Including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test/Makefile | 4 +- app/test/commands.c | 7 + app/test/packet_burst_generator.c | 288 +++ app/test/packet_burst_generator.h | 78 + app/test/test.h | 1 + app/test/test_link_bonding.c | 3958 +++++++++++++++++++++++++++++++++++++ app/test/virtual_pmd.c | 574 ++++++ app/test/virtual_pmd.h | 74 + 8 files changed, 4983 insertions(+), 1 deletions(-) create mode 100644 app/test/packet_burst_generator.c create mode 100644 app/test/packet_burst_generator.h create mode 100644 app/test/test_link_bonding.c create mode 100644 app/test/virtual_pmd.c create mode 100644 app/test/virtual_pmd.h diff --git a/app/test/Makefile b/app/test/Makefile index 3b050c3..02eff0f 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -96,7 +96,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c - +SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c +SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c +SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c ifeq ($(CONFIG_RTE_APP_TEST),y) SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c diff --git a/app/test/commands.c b/app/test/commands.c index 016410d..c31389d 100644 --- a/app/test/commands.c +++ b/app/test/commands.c @@ -157,6 +157,10 @@ static void cmd_autotest_parsed(void *parsed_result, ret = test_timer(); if (!strcmp(res->autotest, "timer_perf_autotest")) ret = test_timer_perf(); +#ifdef RTE_LIBRTE_PMD_BOND + if (!strcmp(res->autotest, "link_bonding_autotest")) + ret = test_link_bonding(); +#endif if (!strcmp(res->autotest, "mempool_autotest")) ret = test_mempool(); if (!strcmp(res->autotest, "mempool_perf_autotest")) @@ -225,6 +229,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest = "alarm_autotest#interrupt_autotest#" "version_autotest#eal_fs_autotest#" "cmdline_autotest#func_reentrancy_autotest#" +#ifdef RTE_LIBRTE_PMD_BOND + "link_bonding_autotest#" +#endif "mempool_perf_autotest#hash_perf_autotest#" "memcpy_perf_autotest#ring_perf_autotest#" "red_autotest#meter_autotest#sched_autotest#" diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c new file mode 100644 index 0000000..38db383 --- /dev/null +++ b/app/test/packet_burst_generator.c @@ -0,0 +1,288 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_byteorder.h> +#include <rte_mbuf.h> + +#include "packet_burst_generator.h" + +#define UDP_SRC_PORT 1024 +#define UDP_DST_PORT 1024 + + +#define IP_DEFTTL 64 /* from RFC 1340. */ +#define IP_VERSION 0x40 +#define IP_HDRLEN 0x05 /* default IP header length == five 32-bits words. */ +#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN) + +static void +copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, + unsigned offset) +{ + struct rte_mbuf *seg; + void *seg_buf; + unsigned copy_len; + + seg = pkt; + while (offset >= seg->pkt.data_len) { + offset -= seg->pkt.data_len; + seg = seg->pkt.next; + } + copy_len = seg->pkt.data_len - offset; + seg_buf = ((char *) seg->pkt.data + offset); + while (len > copy_len) { + rte_memcpy(seg_buf, buf, (size_t) copy_len); + len -= copy_len; + buf = ((char *) buf + copy_len); + seg = seg->pkt.next; + seg_buf = seg->pkt.data; + } + rte_memcpy(seg_buf, buf, (size_t) len); +} + +static inline void +copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) +{ + if (offset + len <= pkt->pkt.data_len) { + rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len); + return; + } + copy_buf_to_pkt_segs(buf, len, pkt, offset); +} + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id) +{ + ether_addr_copy(dst_mac, ð_hdr->d_addr); + ether_addr_copy(src_mac, ð_hdr->s_addr); + + if (vlan_enabled) { + struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr + + sizeof(struct ether_hdr)); + + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + + vhdr->eth_proto = rte_cpu_to_be_16(ETHER_TYPE_IPv4); + vhdr->vlan_tci = van_id; + } else { + eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); + } + +} + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr)); + + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len); + udp_hdr->dgram_cksum = 0; /* No UDP checksum. */ + + return pkt_len; +} + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len) +{ + ip_hdr->vtc_flow = 0; + ip_hdr->payload_len = pkt_data_len; + ip_hdr->proto = IPPROTO_UDP; + ip_hdr->hop_limits = IP_DEFTTL; + + rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr)); + rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr)); + + return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr)); +} + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len) +{ + uint16_t pkt_len; + uint16_t *ptr16; + uint32_t ip_cksum; + + /* + * Initialize IP header. + */ + pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr)); + + ip_hdr->version_ihl = IP_VHL_DEF; + ip_hdr->type_of_service = 0; + ip_hdr->fragment_offset = 0; + ip_hdr->time_to_live = IP_DEFTTL; + ip_hdr->next_proto_id = IPPROTO_UDP; + ip_hdr->packet_id = 0; + ip_hdr->total_length = rte_cpu_to_be_16(pkt_len); + ip_hdr->src_addr = rte_cpu_to_be_32(src_addr); + ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr); + + /* + * Compute IP header checksum. + */ + ptr16 = (uint16_t *)ip_hdr; + ip_cksum = 0; + ip_cksum += ptr16[0]; ip_cksum += ptr16[1]; + ip_cksum += ptr16[2]; ip_cksum += ptr16[3]; + ip_cksum += ptr16[4]; + ip_cksum += ptr16[6]; ip_cksum += ptr16[7]; + ip_cksum += ptr16[8]; ip_cksum += ptr16[9]; + + /* + * Reduce 32 bit checksum to 16 bits and complement it. + */ + ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) + + (ip_cksum & 0x0000FFFF); + ip_cksum %= 65536; + ip_cksum = (~ip_cksum) & 0x0000FFFF; + if (ip_cksum == 0) + ip_cksum = 0xFFFF; + ip_hdr->hdr_checksum = (uint16_t) ip_cksum; + + return pkt_len; +} + + + +/* + * The maximum number of segments per packet is used when creating + * scattered transmit packets composed of a list of mbufs. + */ +#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */ + +#define TXONLY_DEF_PACKET_LEN 64 +#define TXONLY_DEF_PACKET_LEN_128 128 + +uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN; +uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = { + TXONLY_DEF_PACKET_LEN_128, +}; + +uint8_t tx_pkt_nb_segs = 1; + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst) +{ + int i, nb_pkt = 0; + size_t eth_hdr_size; + + struct rte_mbuf *pkt_seg; + struct rte_mbuf *pkt; + + for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) { + pkt = rte_pktmbuf_alloc(mp); + if (pkt == NULL) { +nomore_mbuf: + if (nb_pkt == 0) + return -1; + break; + } + + pkt->pkt.data_len = tx_pkt_seg_lengths[0]; + pkt_seg = pkt; + for (i = 1; i < tx_pkt_nb_segs; i++) { + pkt_seg->pkt.next = rte_pktmbuf_alloc(mp); + if (pkt_seg->pkt.next == NULL) { + pkt->pkt.nb_segs = i; + rte_pktmbuf_free(pkt); + goto nomore_mbuf; + } + pkt_seg = pkt_seg->pkt.next; + pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i]; + } + pkt_seg->pkt.next = NULL; /* Last segment of packet. */ + + /* + * Copy headers in first packet segment(s). + */ + if (vlan_enabled) + eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr); + else + eth_hdr_size = sizeof(struct ether_hdr); + + copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0); + + if (ipv4) { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv4_hdr)); + } else { + copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size); + copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size + + sizeof(struct ipv6_hdr)); + } + + /* + * Complete first mbuf of packet and append it to the + * burst of packets to be transmitted. + */ + pkt->pkt.nb_segs = tx_pkt_nb_segs; + pkt->pkt.pkt_len = tx_pkt_length; + pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size; + + if (ipv4) { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv4; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV4_HDR; + } else { + pkt->pkt.vlan_macip.f.vlan_tci = ETHER_TYPE_IPv6; + pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr); + + if (vlan_enabled) + pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT; + else + pkt->ol_flags = PKT_RX_IPV6_HDR; + } + + pkts_burst[nb_pkt] = pkt; + } + + return nb_pkt; +} + diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h new file mode 100644 index 0000000..5b3cd6c --- /dev/null +++ b/app/test/packet_burst_generator.h @@ -0,0 +1,78 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PACKET_BURST_GENERATOR_H_ +#define PACKET_BURST_GENERATOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + + +#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \ + ((c & 0xff) << 8) | (d & 0xff)) + + +void +initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac, + struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id); + +uint16_t +initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port, + uint16_t dst_port, uint16_t pkt_data_len); + + +uint16_t +initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr, + uint8_t *dst_addr, uint16_t pkt_data_len); + +uint16_t +initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr, + uint32_t dst_addr, uint16_t pkt_data_len); + +int +generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst, + struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr, + uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst); + +#ifdef __cplusplus +} +#endif + + +#endif /* PACKET_BURST_GENERATOR_H_ */ diff --git a/app/test/test.h b/app/test/test.h index f992ceb..574841b 100644 --- a/app/test/test.h +++ b/app/test/test.h @@ -96,6 +96,7 @@ int test_distributor(void); int test_distributor_perf(void); int test_kvargs(void); int test_devargs(void); +int test_link_bonding(void); int test_pci_run; diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c new file mode 100644 index 0000000..6662059 --- /dev/null +++ b/app/test/test_link_bonding.c @@ -0,0 +1,3958 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <errno.h> +#include <sys/queue.h> + +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_debug.h> +#include <rte_ethdev.h> +#include <rte_log.h> +#include <rte_lcore.h> +#include <rte_memory.h> +#include <rte_string_fns.h> +#include <rte_eth_bond.h> + +#include "virtual_pmd.h" +#include "packet_burst_generator.h" + +#include "test.h" + +#define TEST_MAX_NUMBER_OF_PORTS (16) + +#define RX_RING_SIZE 128 +#define RX_FREE_THRESH 32 +#define RX_PTHRESH 8 +#define RX_HTHRESH 8 +#define RX_WTHRESH 0 + +#define TX_RING_SIZE 512 +#define TX_FREE_THRESH 32 +#define TX_PTHRESH 32 +#define TX_HTHRESH 0 +#define TX_WTHRESH 0 +#define TX_RSBIT_THRESH 32 +#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\ + ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \ + ETH_TXQ_FLAGS_NOXSUMTCP) + +#define MBUF_PAYLOAD_SIZE (2048) +#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM) +#define MBUF_CACHE_SIZE (250) +#define BURST_SIZE (32) + +#define DEFAULT_MBUF_DATA_SIZE (2048) +#define RTE_TEST_RX_DESC_MAX (2048) +#define RTE_TEST_TX_DESC_MAX (2048) +#define MAX_PKT_BURST (512) +#define DEF_PKT_BURST (16) + +#define BONDED_DEV_NAME ("unit_test_bonded_device") + +#define INVALID_SOCKET_ID (-1) +#define INVALID_PORT_ID (-1) +#define INVALID_BONDING_MODE (-1) + + +uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 }; +uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF }; + +struct link_bonding_unittest_params { + int8_t bonded_port_id; + int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS]; + uint8_t bonded_slave_count; + uint8_t bonding_mode; + + uint16_t nb_rx_q; + uint16_t nb_tx_q; + + struct rte_mempool *mbuf_pool; + + struct ether_addr *default_slave_mac; + struct ether_addr *default_bonded_mac; + + /* Packet Headers */ + struct ether_hdr *pkt_eth_hdr; + struct ipv4_hdr *pkt_ipv4_hdr; + struct ipv6_hdr *pkt_ipv6_hdr; + struct udp_hdr *pkt_udp_hdr; + +}; + +static struct ipv4_hdr pkt_ipv4_hdr; +static struct ipv6_hdr pkt_ipv6_hdr; +static struct udp_hdr pkt_udp_hdr; + +static struct link_bonding_unittest_params default_params = { + .bonded_port_id = -1, + .slave_port_ids = { 0 }, + .bonded_slave_count = 0, + .bonding_mode = BONDING_MODE_ROUND_ROBIN, + + .nb_rx_q = 1, + .nb_tx_q = 1, + + .mbuf_pool = NULL, + + .default_slave_mac = (struct ether_addr *)slave_mac, + .default_bonded_mac = (struct ether_addr *)bonded_mac, + + .pkt_eth_hdr = NULL, + .pkt_ipv4_hdr = &pkt_ipv4_hdr, + .pkt_ipv6_hdr = &pkt_ipv6_hdr, + .pkt_udp_hdr = &pkt_udp_hdr + +}; + +static struct link_bonding_unittest_params *test_params = &default_params; + +static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB }; + +static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98); +static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97); + +static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA }; +static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB }; + +static uint16_t src_port = 1024; +static uint16_t dst_port_0 = 1024; +static uint16_t dst_port_1 = 2024; + +static uint16_t vlan_id = 0x100; + +struct rte_eth_rxmode rx_mode = { + .max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */ + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled. */ + .hw_ip_checksum = 0, /**< IP checksum offload disabled. */ + .hw_vlan_filter = 1, /**< VLAN filtering enabled. */ + .hw_vlan_strip = 1, /**< VLAN strip enabled. */ + .hw_vlan_extend = 0, /**< Extended VLAN disabled. */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled. */ + .hw_strip_crc = 0, /**< CRC stripping by hardware disabled. */ +}; + +struct rte_fdir_conf fdir_conf = { + .mode = RTE_FDIR_MODE_NONE, + .pballoc = RTE_FDIR_PBALLOC_64K, + .status = RTE_FDIR_REPORT_STATUS, + .flexbytes_offset = 0x6, + .drop_queue = 127, +}; + +static struct rte_eth_conf default_pmd_conf = { + .rxmode = { + .mq_mode = ETH_MQ_RX_NONE, + .max_rx_pkt_len = ETHER_MAX_LEN, + .split_hdr_size = 0, + .header_split = 0, /**< Header Split disabled */ + .hw_ip_checksum = 0, /**< IP checksum offload enabled */ + .hw_vlan_filter = 0, /**< VLAN filtering disabled */ + .jumbo_frame = 0, /**< Jumbo Frame Support disabled */ + .hw_strip_crc = 0, /**< CRC stripped by hardware */ + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +static const struct rte_eth_rxconf rx_conf_default = { + .rx_thresh = { + .pthresh = RX_PTHRESH, + .hthresh = RX_HTHRESH, + .wthresh = RX_WTHRESH, + }, + .rx_free_thresh = RX_FREE_THRESH, + .rx_drop_en = 0, +}; + +static struct rte_eth_txconf tx_conf_default = { + .tx_thresh = { + .pthresh = TX_PTHRESH, + .hthresh = TX_HTHRESH, + .wthresh = TX_WTHRESH, + }, + .tx_free_thresh = TX_FREE_THRESH, + .tx_rs_thresh = TX_RSBIT_THRESH, + .txq_flags = TX_Q_FLAGS + +}; + +static int +configure_ethdev(uint8_t port_id, uint8_t start) +{ + int q_id; + + if (rte_eth_dev_configure(port_id, test_params->nb_rx_q, + test_params->nb_tx_q, &default_pmd_conf) != 0) { + goto error; + } + + for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) { + if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &rx_conf_default, + test_params->mbuf_pool) < 0) { + goto error; + } + } + + for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) { + if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE, + rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) { + printf("Failed to setup tx queue (%d).\n", q_id); + goto error; + } + } + + if (start) { + if (rte_eth_dev_start(port_id) < 0) { + printf("Failed to start device (%d).\n", port_id); + goto error; + } + } + return 0; + +error: + printf("Failed to configure ethdev %d", port_id); + return -1; +} + + +static int +test_setup(void) +{ + int i, retval, nb_mbuf_per_pool; + struct ether_addr *mac_addr = (struct ether_addr *)slave_mac; + + /* Allocate ethernet packet header with space for VLAN header */ + test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) + + sizeof(struct vlan_hdr)); + if (test_params->pkt_eth_hdr == NULL) { + printf("ethernet header struct allocation failed!\n"); + return -1; + } + + + nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST + + RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST; + + test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool, + MBUF_SIZE, MBUF_CACHE_SIZE, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, NULL, + rte_socket_id(), 0); + if (test_params->mbuf_pool == NULL) { + printf("rte_mempool_create failed\n"); + return -1; + } + + /* Create / Initialize virtual eth devs */ + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i; + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d", i); + + test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name, + mac_addr, rte_socket_id()); + if (test_params->slave_port_ids[i] < 0) { + printf("Failed to create virtual pmd eth device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->slave_port_ids[i], 1); + if (retval != 0) { + printf("Failed to configure virtual pmd eth device.\n"); + return -1; + } + } + + return 0; +} + +static int +test_create_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME, + test_params->bonding_mode, rte_socket_id()); + if (test_params->bonded_port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return -1; + } + + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonded pmd eth device.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of slaves is great than expected.\n"); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count > 0) { + printf("Number of active slaves is great than expected.\n"); + return -1; + } + + + if (rte_eth_bond_mode_get(test_params->bonded_port_id) != + test_params->bonding_mode) { + printf("Bonded device mode not as expected.\n"); + return -1; + + } + + return 0; +} + + +static int +test_create_bonded_device_with_invalid_params(void) +{ + int port_id; + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid name */ + port_id = rte_eth_bond_create(NULL, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = INVALID_BONDING_MODE; + + /* Invalid bonding mode */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + rte_socket_id()); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN; + + /* Invalid socket id */ + port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode, + INVALID_SOCKET_ID); + if (port_id >= 0) { + printf("Created bonded device unexpectedly.\n"); + return -1; + } + + return 0; +} + +static int +test_add_slave_to_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval != 0) { + printf("Failed to add slave (%d) to bonded port (%d).\n", + test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count + 1) { + printf("Number of slaves (%d) is greater than expected (%d).\n", + current_slave_count, test_params->bonded_slave_count + 1); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + current_slave_count, 0); + return -1; + } + + test_params->bonded_slave_count++; + + return 0; +} + +static int +test_add_slave_to_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + + +static int +test_remove_slave_from_bonded_device(void) +{ + int retval, current_slave_count; + struct ether_addr read_mac_addr, *mac_addr; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + if (retval != 0) { + printf("\t Failed to remove slave %d from bonded port (%d).\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + test_params->bonded_port_id); + return -1; + } + + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count - 1) { + printf("Number of slaves (%d) is great than expected (%d).\n", + current_slave_count, 0); + return -1; + } + + + mac_addr = (struct ether_addr *)slave_mac; + mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[test_params->bonded_slave_count-1]; + + rte_eth_macaddr_get( + test_params->slave_port_ids[test_params->bonded_slave_count-1], + &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + rte_eth_stats_reset( + test_params->slave_port_ids[test_params->bonded_slave_count-1]); + + virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id, + 0); + + test_params->bonded_slave_count--; + + return 0; +} + +static int +test_remove_slave_from_invalid_bonded_device(void) +{ + int retval; + + /* Invalid port ID */ + retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0], + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + return 0; +} + +static int +test_add_already_bonded_slave_to_bonded_device(void) +{ + int retval, port_id, current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + char pmd_name[RTE_ETH_NAME_MAX_LEN]; + + test_add_slave_to_bonded_device(); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 1) { + printf("Number of slaves (%d) is not that expected (%d).\n", + current_slave_count, 1); + return -1; + } + + rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2", BONDED_DEV_NAME); + + port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode, + rte_socket_id()); + if (port_id < 0) { + printf("Failed to create bonded device.\n"); + return -1; + } + + retval = rte_eth_bond_slave_add(port_id, + test_params->slave_port_ids[test_params->bonded_slave_count - 1]); + if (retval == 0) { + printf("Added slave (%d) to bonded port (%d) unexpectedly.\n", + test_params->slave_port_ids[test_params->bonded_slave_count-1], + port_id); + return -1; + } + + return test_remove_slave_from_bonded_device(); +} + + +static int +test_get_slaves_from_bonded_device(void) +{ + int retval, current_slave_count; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + retval = test_add_slave_to_bonded_device(); + if (retval != 0) + return -1; + + /* Invalid port id */ + current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves, + RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* Invalid slaves pointer */ + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + /* non bonded device*/ + current_slave_count = rte_eth_bond_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS); + if (current_slave_count >= 0) + return -1; + + retval = test_remove_slave_from_bonded_device(); + if (retval != 0) + return -1; + + return 0; +} + + +static int +test_add_remove_multiple_slaves_to_from_bonded_device(void) +{ + int i; + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_add_slave_to_bonded_device() != 0) + return -1; + } + + for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) { + if (test_remove_slave_from_bonded_device() != 0) + return -1; + } + + return 0; +} + +static void +enable_bonded_slaves(void) +{ + int i; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } +} + +static int +test_start_bonded_device(void) +{ + struct rte_eth_link link_status; + + int retval, current_slave_count, current_bonding_mode, primary_port; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + /* Add slave to bonded device*/ + if (test_add_slave_to_bonded_device() != 0) + return -1; + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + /* Change link status of virtual pmd so it will be added to the active + * slave list of the bonded device*/ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[test_params->bonded_slave_count-1], 1); + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (current_bonding_mode != test_params->bonding_mode) { + printf("Bonded device mode (%d) is not expected value (%d).\n", + current_bonding_mode, test_params->bonding_mode); + return -1; + + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) { + printf("Primary port (%d) is not expected value (%d).\n", + primary_port, test_params->slave_port_ids[0]); + return -1; + + } + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (!link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 1); + return -1; + + } + + return 0; +} + +static int +test_stop_bonded_device(void) +{ + int current_slave_count; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + struct rte_eth_link link_status; + + rte_eth_dev_stop(test_params->bonded_port_id); + + rte_eth_link_get(test_params->bonded_port_id, &link_status); + if (link_status.link_status) { + printf("Bonded port (%d) status (%d) is not expected value (%d).\n", + test_params->bonded_port_id, link_status.link_status, 0); + return -1; + } + + current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != test_params->bonded_slave_count) { + printf("Number of slaves (%d) is not expected value (%d).\n", + current_slave_count, test_params->bonded_slave_count); + return -1; + } + + current_slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (current_slave_count != 0) { + printf("Number of active slaves (%d) is not expected value (%d).\n", + current_slave_count, 0); + return -1; + } + + return 0; +} + +static int remove_slaves_and_stop_bonded_device(void) +{ + /* Clean up and remove slaves from bonded device */ + while (test_params->bonded_slave_count > 0) { + if (test_remove_slave_from_bonded_device() != 0) { + printf("test_remove_slave_from_bonded_device failed\n"); + return -1; + } + } + + rte_eth_dev_stop(test_params->bonded_port_id); + rte_eth_stats_reset(test_params->bonded_port_id); + rte_eth_bond_mac_address_reset(test_params->bonded_port_id); + + return 0; +} + +static int +test_set_bonding_mode(void) +{ + int i; + int retval, bonding_mode; + + int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN, + BONDING_MODE_ACTIVE_BACKUP, + BONDING_MODE_BALANCE, + BONDING_MODE_BROADCAST }; + + /* Test supported link bonding modes */ + for (i = 0; i < (int)RTE_DIM(bonding_modes); i++) { + /* Invalid port ID */ + retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0], + bonding_modes[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + bonding_modes[i]); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_modes[i]); + return -1; + } + + bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id); + if (bonding_mode != bonding_modes[i]) { + printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n", + bonding_mode, test_params->bonded_port_id, + bonding_modes[i]); + return -1; + } + + + /* Invalid port ID */ + bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + INVALID_PORT_ID); + return -1; + } + + + /* Non bonded device */ + bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]); + if (bonding_mode >= 0) { + printf("Expected call to failed as invalid port (%d) specified.\n", + test_params->slave_port_ids[0]); + return -1; + } + + } + + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_set_primary_slave(void) +{ + int i, j, retval; + struct ether_addr read_mac_addr; + struct ether_addr *expected_mac_addr; + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, + BONDING_MODE_ROUND_ROBIN); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN); + return -1; + } + + /* Invalid port ID */ + retval = rte_eth_bond_primary_set(INVALID_PORT_ID, + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set slave as primary + * Verify slave it is now primary slave + * Verify that MAC address of bonded device is that of primary slave + * Verify that MAC address of all bonded slaves are that of primary slave + */ + for (i = 0; i < 4; i++) { + + /* Non bonded device */ + retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i], + test_params->slave_port_ids[i]); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[i]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, + test_params->slave_port_ids[i]); + return -1; + } + + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval < 0) { + printf("Failed to read primary port from bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } else if (retval != test_params->slave_port_ids[i]) { + printf("Bonded port (%d) primary port (%d) not expected value (%d)\n", + test_params->bonded_port_id, retval, + test_params->slave_port_ids[i]); + return -1; + } + + /* stop/start bonded eth dev to apply new MAC */ + rte_eth_dev_stop(test_params->bonded_port_id); + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + expected_mac_addr = (struct ether_addr *)&slave_mac; + expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = + test_params->slave_port_ids[i]; + + /* Check primary slave MAC */ + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (j = 0; j < 4; j++) { + if (j != i) { + rte_eth_macaddr_get(test_params->slave_port_ids[j], + &read_mac_addr); + if (memcmp(expected_mac_addr, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + } + } + + + /* Test with none existent port */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + /* Test with slave port */ + retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + /* No slaves */ + retval = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (retval >= 0) { + printf("read primary port from expectedly\n"); + return -1; + } + + return 0; +} + +static int +test_set_explicit_bonded_mac(void) +{ + int i, retval; + struct ether_addr read_mac_addr; + struct ether_addr *mac_addr; + + uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + + mac_addr = (struct ether_addr *)explicit_bonded_mac; + + /* Invalid port ID */ + retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Non bonded device */ + retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0], + mac_addr); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* NULL MAC address */ + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL); + if (retval == 0) { + printf("Expected call to failed as NULL MAC specified\n"); + return -1; + } + + retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, + mac_addr); + if (retval != 0) { + printf("Failed to set MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + return -1; + } + + /* Add 4 slaves to bonded device */ + for (i = test_params->bonded_slave_count; i < 4; i++) { + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave to bonded device.\n"); + return -1; + } + } + + /* Check bonded MAC */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port mac address not set to that of primary port\n"); + return -1; + } + + /* Check other slaves MACs */ + for (i = 0; i < 4; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port mac address not set to that of primary port\n"); + return -1; + } + } + + /* test resetting mac address on bonded device */ + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) { + printf("Reset MAC address on bonded port (%d) unexpectedly\n", + test_params->slave_port_ids[1]); + + return -1; + } + + /* test resetting mac address on bonded device with no slaves */ + + if (remove_slaves_and_stop_bonded_device() != 0) + return -1; + + if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) { + printf("Failed to reset MAC address on bonded port (%d)\n", + test_params->bonded_port_id); + + return -1; + } + + return 0; +} + + +static int +initialize_bonded_device_with_slaves(uint8_t bonding_mode, + uint8_t number_of_slaves, uint8_t enable_slave) +{ + int retval; + + /* configure bonded device */ + retval = configure_ethdev(test_params->bonded_port_id, 0); + if (retval != 0) { + printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n", + test_params->bonded_port_id, bonding_mode, number_of_slaves); + return -1; + } + + while (number_of_slaves > test_params->bonded_slave_count) { + /* Add slaves to bonded device */ + retval = test_add_slave_to_bonded_device(); + if (retval != 0) { + printf("Failed to add slave (%d to bonding port (%d).\n", + test_params->bonded_slave_count - 1, + test_params->bonded_port_id); + return -1; + } + } + + /* Set link bonding mode */ + retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode); + if (retval != 0) { + printf("Failed to set link bonding mode on port (%d) to (%d).\n", + test_params->bonded_port_id, bonding_mode); + return -1; + } + + retval = rte_eth_dev_start(test_params->bonded_port_id); + if (retval != 0) { + printf("Failed to start bonded pmd eth device.\n"); + return -1; + } + + if (enable_slave) + enable_bonded_slaves(); + + return 0; +} + +static int +test_adding_slave_after_bonded_device_started(void) +{ + int i; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) != + 0) + return -1; + + /* Enabled slave devices */ + for (i = 0; i < test_params->bonded_slave_count + 1; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 1); + } + + if (rte_eth_bond_slave_add(test_params->bonded_port_id, + test_params->slave_port_ids[test_params->bonded_slave_count]) != + 0) { + printf("\t Failed to add slave to bonded port.\n"); + return -1; + } + + test_params->bonded_slave_count++; + + return remove_slaves_and_stop_bonded_device(); +} + +static int +generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size, + uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac, + uint8_t toggle_ip_addr, uint8_t toggle_udp_port) +{ + uint16_t pktlen, generated_burst_size; + void *ip_hdr; + + if (toggle_dst_mac) + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, + vlan, vlan_id); + else + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, + vlan, vlan_id); + + + if (toggle_udp_port) + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_1, 64); + else + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 64); + + if (ipv4) { + if (toggle_ip_addr) + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_1, pktlen); + else + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + ip_hdr = test_params->pkt_ipv4_hdr; + } else { + if (toggle_ip_addr) + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1, + pktlen); + else + pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr, + (uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0, + pktlen); + + ip_hdr = test_params->pkt_ipv6_hdr; + } + + /* Generate burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4, + test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) { + printf("Failed to generate packet burst"); + return -1; + } + + return generated_burst_size; +} + +/** Round Robin Mode Tests */ + +static int +test_roundrobin_tx_burst(void) +{ + int i, burst_size, nb_tx; + struct rte_mbuf *pkt_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1) + != 0) + return -1; + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != + (uint64_t)burst_size / test_params->bonded_slave_count) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_rx_burst_on_single_slave(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int i, j, nb_rx, burst_size = 25; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + + /* Verify bonded slave devices rx count */ + /* Verify slave ports tx stats */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + + /* Reset bonded slaves stats */ + rte_eth_stats_reset(test_params->slave_port_ids[j]); + } + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (gen_pkt_burst[i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[i]); + + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3) + +static int +test_roundrobin_rx_burst_on_multiple_slaves(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 }; + int i, nb_rx; + + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slaves */ + for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed (%d != %d)\n", nb_rx, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) + != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded dev */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagate to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_2, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_roundrobin_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) != + 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4) +#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2) + +static int +test_roundrobin_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, burst_size, slave_count; + + /* NULL all pointers in array to simplify cleanup */ + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves + * in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, + TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves eth_devs link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != + TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT); + return -1; + } + + burst_size = 21; + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test burst of traffic + * 2. Transmit burst on bonded eth_dev + * 3. Verify stats for bonded eth_dev (opackets = burst_size) + * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst, + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 11) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != 10) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Verify that pkts are not sent on slaves with link status down: + * + * 1. Generate test bursts of traffic + * 2. Add bursts on to virtual eth_devs + * 3. Rx burst on bonded eth_dev, expected (burst_ size * + * TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received + * 4. Verify stats for bonded eth_dev + * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0) + */ + for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) + rte_pktmbuf_free(rx_pkt_burst[i]); + + if (gen_pkt_burst[1][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + + if (gen_pkt_burst[3][i] != NULL) + rte_pktmbuf_free(gen_pkt_burst[1][i]); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Active Backup Mode Tests */ + +static int +test_activebackup_tx_burst(void) +{ + int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (test_params->slave_port_ids[i] == primary_port) { + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, + burst_size / test_params->bonded_slave_count); + return -1; + } + } else { + if (port_stats.opackets != 0) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, 0); + return -1; + } + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4) + +static int +test_activebackup_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + + struct rte_eth_stats port_stats; + + int primary_port; + + int i, j, nb_rx, burst_size = 17; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0) + != burst_size) { + return -1; + } + + /* Add rx data to slave */ + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[0], burst_size); + + /* Call rx burst on bonded device */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, + &rx_pkt_burst[0], MAX_PKT_BURST); + if (nb_rx < 0) { + printf("rte_eth_rx_burst failed\n"); + return -1; + } + + if (test_params->slave_port_ids[i] == primary_port) { + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + + /* Verify bonded slave devices rx count */ + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (i == j) { + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, burst_size); + return -1; + } + } else { + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + } else { + for (j = 0; j < test_params->bonded_slave_count; j++) { + rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[i], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + } + } + + /* free mbufs */ + for (i = 0; i < MAX_PKT_BURST; i++) { + if (rx_pkt_burst[i] != NULL) { + rte_pktmbuf_free(rx_pkt_burst[i]); + rx_pkt_burst[i] = NULL; + } + } + + /* reset bonded device stats */ + rte_eth_stats_reset(test_params->bonded_port_id); + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_promiscuous_enable_disable(void) +{ + int i, primary_port, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1) + != 0) + return -1; + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port < 0) { + printf("failed to get primary slave for bonded port (%d)", + test_params->bonded_port_id); + } + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (primary_port == test_params->slave_port_ids[i]) { + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } else { + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1) + != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) { + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_activebackup_verify_slave_link_status_change_failover(void) +{ + struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count, primary_port; + + burst_size = 21; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Generate packet burst for testing */ + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1) + != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[0]) + printf("Primary port not as expected"); + + /* Bring 2 slaves down and verify active slave count */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + + /* Bring primary port down, verify that active slave count is 3 and primary + * has changed */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 3) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 3); + return -1; + } + + primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id); + if (primary_port != test_params->slave_port_ids[2]) + printf("Primary port not as expected"); + + /* Verify that pkts are sent on new primary slave */ + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size) + != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* Generate packet burst for testing */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue( + test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size); + } + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + /* free mbufs */ + + for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Balance Mode Tests */ + +static int +test_balance_xmit_policy_configuration(void) +{ + int retval; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, + 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + /* Invalid port id */ + retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID, + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + /* Set xmit policy on non bonded device */ + retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0], + BALANCE_XMIT_POLICY_LAYER2); + if (retval == 0) { + printf("Expected call to failed as invalid port specified.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER2) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER23) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) != + BALANCE_XMIT_POLICY_LAYER34) { + printf("balance xmit policy not as expected.\n"); + return -1; + } + + /* Invalid port id */ + if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2) + +static int +test_balance_l2_tx_burst(void) +{ + struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 }; + + uint16_t pktlen; + + int retval, i; + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + /* Generate a burst 1 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0]) + return -1; + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0); + + /* Generate a burst 2 of packets to transmit */ + if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0], + test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1, + test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1]) + return -1; + + /* Send burst 1 on bonded port */ + for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) { + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0], + burst_size[i]) != burst_size[i]) + return -1; + } + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]); + return -1; + } + + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], ( + unsigned int)port_stats.opackets, burst_size[1]); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0], + burst_size[0]) != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER23); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, + 0, 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 1, 1, 0); +} + +static int +test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 1, 0, 1); +} + +static int +test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(0, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l23_tx_burst(1, 0, 0, 1); +} + +static int +test_balance_l23_tx_burst_toggle_mac_addr(void) +{ + return balance_l23_tx_burst(0, 0, 1, 0); +} + +static int +balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4, + uint8_t toggle_mac_addr, uint8_t toggle_ip_addr, + uint8_t toggle_udp_port) +{ + int retval, i; + int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2; + + struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST]; + struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER34); + if (retval != 0) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + burst_size_1 = 20; + burst_size_2 = 10; + + if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate test bursts of packets to transmit */ + if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0, + 0, 0) != burst_size_1) + return -1; + + if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4, + toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2) + return -1; + + /* Send burst 1 on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != burst_size_1) + return -1; + + /* Send burst 2 on bonded port */ + nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2, + burst_size_2); + if (nb_tx_2 != burst_size_2) + return -1; + + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2); + return -1; + } + + /* Verify slave ports tx stats */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_1) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.opackets, nb_tx_1); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != (uint64_t)nb_tx_2) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.opackets, nb_tx_2); + return -1; + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1, + burst_size_1); + if (nb_tx_1 != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv4_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 1, 0, 0, 1); +} + +static int +test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 1, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(0, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void) +{ + return balance_l34_tx_burst(1, 0, 0, 1, 0); +} + +static int +test_balance_l34_tx_burst_ipv6_toggle_udp_port(void) +{ + return balance_l34_tx_burst(0, 0, 0, 0, 1); +} + +#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3) + +static int +test_balance_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1) + != 0) + return -1; + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, + 0, 0) != burst_size[i]) + return -1; + } + /* Add rx data to slaves */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("balance rx burst failed\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_balance_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1); + + /* Initialize bonded device with 2 slaves in active backup mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0) + return -1; + + /* Verify that bonded MACs is that of first slave and that the other slave + * MAC hasn't been changed */ + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* change primary and verify that MAC addresses haven't changed */ + if (rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[1]) != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of primary port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of bonded port\n", + test_params->bonded_port_id); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr); + if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of bonded port\n", + test_params->slave_port_ids[1]); + return -1; + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4) + +static int +test_balance_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0) + return -1; + + if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id, + BALANCE_XMIT_POLICY_LAYER2)) { + printf("Failed to set balance xmit policy.\n"); + return -1; + } + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of slaves (%d) is not as expected (%d).\n", slave_count, + TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS) != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + /* Send to sets of packet burst and verify that they are balanced across + * slaves */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[2], (int)port_stats.opackets, + burst_size); + return -1; + } + + /* verify that all packets get send on primary slave when no other slaves + * are available */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 0); + + if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS) != 1) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 1); + return -1; + } + + if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0], + burst_size) != burst_size) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size + + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size + burst_size + burst_size); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d).\n", + test_params->slave_port_ids[0], (int)port_stats.opackets, + burst_size + burst_size); + return -1; + } + + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[0], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[2], 1); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 1); + + for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) != + burst_size) + return -1; + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + + + /* Verify that pkts are not received on slaves with link status down */ + + rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size * 3)) { + printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.ipackets, + burst_size * 3); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +/** Broadcast Mode Tests */ + +static int +test_broadcast_tx_burst(void) +{ + int i, pktlen, retval, burst_size, generated_burst_size, nb_tx; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + + struct rte_eth_stats port_stats; + + retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1); + if (retval != 0) { + printf("Failed to initialize_bonded_device_with_slaves.\n"); + return -1; + } + + initialize_eth_header(test_params->pkt_eth_hdr, + (struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0); + + pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port, + dst_port_0, 16); + pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr, + dst_addr_0, pktlen); + + burst_size = 20 * test_params->bonded_slave_count; + + if (burst_size > MAX_PKT_BURST) { + printf("Burst size specified is greater than supported.\n"); + return -1; + } + + /* Generate a burst of packets to transmit */ + generated_burst_size = generate_packet_burst(test_params->mbuf_pool, + pkts_burst, test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, + 1, test_params->pkt_udp_hdr, burst_size); + if (generated_burst_size != burst_size) + return -1; + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != burst_size * test_params->bonded_slave_count) { + printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + nb_tx, burst_size); + return -1; + } + + /* Verify bonded port tx stats */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)burst_size * + test_params->bonded_slave_count) { + printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.opackets, + burst_size); + } + + /* Verify slave ports tx stats */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, + (unsigned int)port_stats.opackets, burst_size); + } + } + + /* Put all slaves down and try and transmit */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[i], 0); + } + + /* Send burst on bonded port */ + nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst, + burst_size); + if (nb_tx != 0) + return -1; + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3) + +static int +test_broadcast_rx_burst(void) +{ + struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST]; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 }; + int i, j, nb_rx; + + memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0) + return -1; + + + /* Generate test bursts of packets to transmit */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0, + 0) != burst_size[i]) + return -1; + } + + /* Add rx data to slave 0 */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &gen_pkt_burst[i][0], burst_size[i]); + } + + + /* Call rx burst on bonded device */ + /* Send burst on bonded port */ + nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST); + if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) { + printf("round-robin rx burst failed"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] + + burst_size[2])) { + printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->bonded_port_id, (unsigned int)port_stats.ipackets, + burst_size[0] + burst_size[1] + burst_size[2]); + return -1; + } + + + /* Verify bonded slave devices rx counts */ + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[0]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[0], + (unsigned int)port_stats.ipackets, burst_size[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[1]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[1], + (unsigned int)port_stats.ipackets, burst_size[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.ipackets != (uint64_t)burst_size[2]) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[2], + (unsigned int)port_stats.ipackets, + burst_size[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.ipackets != 0) { + printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n", + test_params->slave_port_ids[3], + (unsigned int)port_stats.ipackets, 0); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (gen_pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(gen_pkt_burst[i][j]); + gen_pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_promiscuous_enable_disable(void) +{ + int i, promiscuous_en; + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0) + return -1; + + rte_eth_promiscuous_enable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 1) { + printf("Port (%d) promiscuous mode not enabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 1) { + printf("slave port (%d) promiscuous mode not enabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + rte_eth_promiscuous_disable(test_params->bonded_port_id); + + promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id); + if (promiscuous_en != 0) { + printf("Port (%d) promiscuous mode not disabled\n", + test_params->bonded_port_id); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + promiscuous_en = rte_eth_promiscuous_get( + test_params->slave_port_ids[i]); + if (promiscuous_en != 0) { + printf("slave port (%d) promiscuous mode not disabled\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_broadcast_verify_mac_assignment(void) +{ + struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1; + + int i, retval; + + rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0); + rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0) + return -1; + + /* Verify that all MACs are the same as first slave added to bonded + * device */ + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* change primary and verify that MAC addresses haven't changed */ + retval = rte_eth_bond_primary_set(test_params->bonded_port_id, + test_params->slave_port_ids[2]); + if (retval != 0) { + printf("Failed to set bonded port (%d) primary port to (%d)\n", + test_params->bonded_port_id, test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_0, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* stop / start bonded device and verify that primary MAC address is + * propagated to bonded device and slaves */ + + rte_eth_dev_stop(test_params->bonded_port_id); + + if (rte_eth_dev_start(test_params->bonded_port_id) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(&expected_mac_addr_1, &read_mac_addr, + sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Set explicit MAC address */ + if (rte_eth_bond_mac_address_set(test_params->bonded_port_id, + (struct ether_addr *)bonded_mac) != 0) + return -1; + + rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("bonded port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) { + rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr); + if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) { + printf("slave port (%d) mac address not set to that of new primary port\n", + test_params->slave_port_ids[i]); + return -1; + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4) +static int +test_broadcast_verify_slave_link_status_change_behaviour(void) +{ + struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST]; + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL }; + struct rte_eth_stats port_stats; + + uint8_t slaves[RTE_MAX_ETHPORTS]; + + int i, j, burst_size, slave_count; + + memset(pkt_burst, 0, sizeof(pkt_burst)); + + /* Initialize bonded device with 4 slaves in round robin mode */ + if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, + BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0) + return -1; + + /* Verify Current Slaves Count /Active Slave Count is */ + slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves, + RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + slave_count = rte_eth_bond_active_slaves_get( + test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS); + if (slave_count != 4) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 4); + return -1; + } + + /* Set 2 slaves link status to down */ + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[1], 0); + virtual_ethdev_simulate_link_status_interrupt( + test_params->slave_port_ids[3], 0); + + slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id, + slaves, RTE_MAX_ETHPORTS); + if (slave_count != 2) { + printf("Number of active slaves (%d) is not as expected (%d).\n", + slave_count, 2); + return -1; + } + + for (i = 0; i < test_params->bonded_slave_count; i++) + rte_eth_stats_reset(test_params->slave_port_ids[i]); + + /* Verify that pkts are not sent on slaves with link status down */ + burst_size = 21; + + if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + printf("generate_test_burst failed\n"); + return -1; + } + + if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], + burst_size) != (burst_size * slave_count)) { + printf("rte_eth_tx_burst failed\n"); + return -1; + } + + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) { + printf("(%d) port_stats.opackets (%d) not as expected (%d)\n", + test_params->bonded_port_id, (int)port_stats.opackets, + burst_size * slave_count); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[0]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[1]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats); + if (port_stats.opackets != (uint64_t)burst_size) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[2]); + return -1; + } + + rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats); + if (port_stats.opackets != 0) { + printf("(%d) port_stats.opackets not as expected\n", + test_params->slave_port_ids[3]); + return -1; + } + + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) != + burst_size) { + return -1; + } + + virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i], + &pkt_burst[i][0], burst_size); + } + + /* Verify that pkts are not received on slaves with link status down */ + + if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst, + MAX_PKT_BURST) != + burst_size + burst_size) { + printf("rte_eth_rx_burst\n"); + return -1; + } + + /* Verify bonded device rx count */ + rte_eth_stats_get(test_params->bonded_port_id, &port_stats); + if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) { + printf("(%d) port_stats.ipackets not as expected\n", + test_params->bonded_port_id); + return -1; + } + + /* free mbufs allocate for rx testing */ + for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) { + for (j = 0; j < MAX_PKT_BURST; j++) { + if (pkt_burst[i][j] != NULL) { + rte_pktmbuf_free(pkt_burst[i][j]); + pkt_burst[i][j] = NULL; + } + } + } + + /* Clean up and remove slaves from bonded device */ + return remove_slaves_and_stop_bonded_device(); +} + +static int +test_reconfigure_bonded_device(void) +{ + test_params->nb_rx_q = 4; + test_params->nb_tx_q = 4; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device"); + return -1; + } + + + test_params->nb_rx_q = 2; + test_params->nb_tx_q = 2; + + if (configure_ethdev(test_params->bonded_port_id, 0) != 0) { + printf("failed to reconfigure bonded device with less rx/tx queues"); + return -1; + } + + return 0; +} + + +static int +test_close_bonded_device(void) +{ + rte_eth_dev_close(test_params->bonded_port_id); + return 0; +} + +static int +testsuite_teardown(void) +{ + if (test_params->pkt_eth_hdr != NULL) + free(test_params->pkt_eth_hdr); + + return 0; +} + +struct unittest { + int (*test_function)(void); + const char *success_msg; + const char *fail_msg; +}; + +struct unittest_suite { + int (*setup_function)(void); + int (*teardown_function)(void); + struct unittest unittests[]; +}; + +static struct unittest_suite link_bonding_test_suite = { + .setup_function = test_setup, + .teardown_function = testsuite_teardown, + .unittests = { + { test_create_bonded_device, "test_create_bonded_device succeeded", + "test_create_bonded_device failed" }, + { test_create_bonded_device_with_invalid_params, + "test_create_bonded_device_with_invalid_params succeeded", + "test_create_bonded_device_with_invalid_params failed" }, + { test_add_slave_to_bonded_device, + "test_add_slave_to_bonded_device succeeded", + "test_add_slave_to_bonded_device failed" }, + { test_add_slave_to_invalid_bonded_device, + "test_add_slave_to_invalid_bonded_device succeeded", + "test_add_slave_to_invalid_bonded_device failed" }, + { test_remove_slave_from_bonded_device, + "test_remove_slave_from_bonded_device succeeded ", + "test_remove_slave_from_bonded_device failed" }, + { test_remove_slave_from_invalid_bonded_device, + "test_remove_slave_from_invalid_bonded_device succeeded", + "test_remove_slave_from_invalid_bonded_device failed" }, + { test_get_slaves_from_bonded_device, + "test_get_slaves_from_bonded_device succeeded", + "test_get_slaves_from_bonded_device failed" }, + { test_add_already_bonded_slave_to_bonded_device, + "test_add_already_bonded_slave_to_bonded_device succeeded", + "test_add_already_bonded_slave_to_bonded_device failed" }, + { test_add_remove_multiple_slaves_to_from_bonded_device, + "test_add_remove_multiple_slaves_to_from_bonded_device succeeded", + "test_add_remove_multiple_slaves_to_from_bonded_device failed" }, + { test_start_bonded_device, + "test_start_bonded_device succeeded", + "test_start_bonded_device failed" }, + { test_stop_bonded_device, + "test_stop_bonded_device succeeded", + "test_stop_bonded_device failed" }, + { test_set_bonding_mode, + "test_set_bonding_mode succeeded", + "test_set_bonding_mode failed" }, + { test_set_primary_slave, + "test_set_primary_slave succeeded", + "test_set_primary_slave failed" }, + { test_set_explicit_bonded_mac, + "test_set_explicit_bonded_mac succeeded", + "test_set_explicit_bonded_mac failed" }, + { test_adding_slave_after_bonded_device_started, + "test_adding_slave_after_bonded_device_started succeeded", + "test_adding_slave_after_bonded_device_started failed" }, + { test_roundrobin_tx_burst, + "test_roundrobin_tx_burst succeeded", + "test_roundrobin_tx_burst failed" }, + { test_roundrobin_rx_burst_on_single_slave, + "test_roundrobin_rx_burst_on_single_slave succeeded", + "test_roundrobin_rx_burst_on_single_slave failed" }, + { test_roundrobin_rx_burst_on_multiple_slaves, + "test_roundrobin_rx_burst_on_multiple_slaves succeeded", + "test_roundrobin_rx_burst_on_multiple_slaves failed" }, + { test_roundrobin_verify_promiscuous_enable_disable, + "test_roundrobin_verify_promiscuous_enable_disable succeeded", + "test_roundrobin_verify_promiscuous_enable_disable failed" }, + { test_roundrobin_verify_mac_assignment, + "test_roundrobin_verify_mac_assignment succeeded", + "test_roundrobin_verify_mac_assignment failed" }, + { test_roundrobin_verify_slave_link_status_change_behaviour, + "test_roundrobin_verify_slave_link_status_change_behaviour succeeded", + "test_roundrobin_verify_slave_link_status_change_behaviour failed" }, + { test_activebackup_tx_burst, + "test_activebackup_tx_burst succeeded", + "test_activebackup_tx_burst failed" }, + { test_activebackup_rx_burst, + "test_activebackup_rx_burst succeeded", + "test_activebackup_rx_burst failed" }, + { test_activebackup_verify_promiscuous_enable_disable, + "test_activebackup_verify_promiscuous_enable_disable succeeded", + "test_activebackup_verify_promiscuous_enable_disable failed" }, + { test_activebackup_verify_mac_assignment, + "test_activebackup_verify_mac_assignment succeeded", + "test_activebackup_verify_mac_assignment failed" }, + { test_activebackup_verify_slave_link_status_change_failover, + "test_activebackup_verify_slave_link_status_change_failover succeeded", + "test_activebackup_verify_slave_link_status_change_failover failed" }, + { test_balance_xmit_policy_configuration, + "test_balance_xmit_policy_configuration succeeded", + "test_balance_xmit_policy_configuration failed" }, + { test_balance_l2_tx_burst, + "test_balance_l2_tx_burst succeeded", + "test_balance_l2_tx_burst failed" }, + { test_balance_l23_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l23_tx_burst_toggle_mac_addr, + "test_balance_l23_tx_burst_toggle_mac_addr succeeded", + "test_balance_l23_tx_burst_toggle_mac_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv4_toggle_udp_port, + "test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" }, + { test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr, + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded", + "test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" }, + { test_balance_l34_tx_burst_ipv6_toggle_udp_port, + "test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded", + "test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" }, + { test_balance_rx_burst, + "test_balance_rx_burst succeeded", + "test_balance_rx_burst failed" }, + { test_balance_verify_promiscuous_enable_disable, + "test_balance_verify_promiscuous_enable_disable succeeded", + "test_balance_verify_promiscuous_enable_disable failed" }, + { test_balance_verify_mac_assignment, + "test_balance_verify_mac_assignment succeeded", + "test_balance_verify_mac_assignment failed" }, + { test_balance_verify_slave_link_status_change_behaviour, + "test_balance_verify_slave_link_status_change_behaviour succeeded", + "test_balance_verify_slave_link_status_change_behaviour failed" }, + { test_broadcast_tx_burst, + "test_broadcast_tx_burst succeeded", + "test_broadcast_tx_burst failed" }, + { test_broadcast_rx_burst, + "test_broadcast_rx_burst succeeded", + "test_broadcast_rx_burst failed" }, + { test_broadcast_verify_promiscuous_enable_disable, + "test_broadcast_verify_promiscuous_enable_disable succeeded", + "test_broadcast_verify_promiscuous_enable_disable failed" }, + { test_broadcast_verify_mac_assignment, + "test_broadcast_verify_mac_assignment succeeded", + "test_broadcast_verify_mac_assignment failed" }, + { test_broadcast_verify_slave_link_status_change_behaviour, + "test_broadcast_verify_slave_link_status_change_behaviour succeeded", + "test_broadcast_verify_slave_link_status_change_behaviour failed" }, + { test_reconfigure_bonded_device, + "test_reconfigure_bonded_device succeeded", + "test_reconfigure_bonded_device failed" }, + { test_close_bonded_device, + "test_close_bonded_device succeeded", + "test_close_bonded_device failed" }, + + { NULL , NULL, NULL } /**< NULL terminate unit test array */ + } +}; + + +int +test_link_bonding(void) +{ + int i = 0; + + if (link_bonding_test_suite.setup_function) { + if (link_bonding_test_suite.setup_function() != 0) + return -1; + } + + while (link_bonding_test_suite.unittests[i].test_function) { + if (link_bonding_test_suite.unittests[i].test_function() == 0) { + printf("%s", link_bonding_test_suite.unittests[i].success_msg ? + link_bonding_test_suite.unittests[i].success_msg : + "unit test succeeded"); + } else { + printf("%s", link_bonding_test_suite.unittests[i].fail_msg ? + link_bonding_test_suite.unittests[i].fail_msg : + "unit test failed"); + return -1; + } + printf("\n"); + i++; + } + + if (link_bonding_test_suite.teardown_function) { + if (link_bonding_test_suite.teardown_function() != 0) + return -1; + } + + return 0; +} diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c new file mode 100644 index 0000000..0c67a10 --- /dev/null +++ b/app/test/virtual_pmd.c @@ -0,0 +1,574 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rte_mbuf.h> +#include <rte_ethdev.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_memory.h> + +#include "virtual_pmd.h" + +#define MAX_PKT_BURST 512 + +static const char *virtual_ethdev_driver_name = "Virtual PMD"; + +struct virtual_ethdev_private { + struct rte_eth_stats eth_stats; + + struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST]; + int rx_pkt_burst_len; +}; + +struct virtual_ethdev_queue { + int port_id; + int queue_id; +}; + +static int +virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 1; + + return 0; +} + +static int +virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_started = 0; + + return -1; +} +static void virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused) +{ + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_started = 0; +} + +static void +virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused) +{} + +static int +virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->driver_name = virtual_ethdev_driver_name; + dev_info->max_mac_addrs = 1; + + dev_info->max_rx_pktlen = (uint32_t)2048; + + dev_info->max_rx_queues = (uint16_t)128; + dev_info->max_tx_queues = (uint16_t)512; + + dev_info->min_rx_bufsize = 0; + dev_info->pci_dev = NULL; +} + +static int +virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + struct virtual_ethdev_queue *rx_q; + + rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (rx_q == NULL) + return -1; + + rx_q->port_id = dev->data->port_id; + rx_q->queue_id = rx_queue_id; + + dev->data->rx_queues[rx_queue_id] = rx_q; + + return 0; +} + +static int +virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool __rte_unused) +{ + return -1; +} + +static int +virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev, + uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct virtual_ethdev_queue *tx_q; + + tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL, + sizeof(struct virtual_ethdev_queue), 0, socket_id); + + if (tx_q == NULL) + return -1; + + tx_q->port_id = dev->data->port_id; + tx_q->queue_id = tx_queue_id; + + dev->data->tx_queues[tx_queue_id] = tx_q; + + return 0; +} + +static int +virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused, + uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_rx_queue_release(void *q __rte_unused) +{ +} + +static void +virtual_ethdev_tx_queue_release(void *q __rte_unused) +{ +} + +static int +virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev, + int wait_to_complete __rte_unused) +{ + if (!bonded_eth_dev->data->dev_started) + bonded_eth_dev->data->dev_link.link_status = 0; + + return 0; +} + +static int +virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return -1; +} + +static void +virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + if (stats) + rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats)); +} + +static void +virtual_ethdev_stats_reset(struct rte_eth_dev *dev) +{ + struct virtual_ethdev_private *dev_private = dev->data->dev_private; + + dev_private->rx_pkt_burst_len = 0; + + /* Reset internal statistics */ + memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats)); +} + +static void +virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused) +{} + +static void +virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused) +{} + + +static struct eth_dev_ops virtual_ethdev_default_dev_ops = { + .dev_configure = virtual_ethdev_configure_success, + .dev_start = virtual_ethdev_start_success, + .dev_stop = virtual_ethdev_stop, + .dev_close = virtual_ethdev_close, + .dev_infos_get = virtual_ethdev_info_get, + .rx_queue_setup = virtual_ethdev_rx_queue_setup_success, + .tx_queue_setup = virtual_ethdev_tx_queue_setup_success, + .rx_queue_release = virtual_ethdev_rx_queue_release, + .tx_queue_release = virtual_ethdev_tx_queue_release, + .link_update = virtual_ethdev_link_update_success, + .stats_get = virtual_ethdev_stats_get, + .stats_reset = virtual_ethdev_stats_reset, + .promiscuous_enable = virtual_ethdev_promiscuous_mode_enable, + .promiscuous_disable = virtual_ethdev_promiscuous_mode_disable +}; + + +void +virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success; + else + vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail; + +} + +void +virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success; + else + vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail; +} + +void +virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->rx_queue_setup = + virtual_ethdev_rx_queue_setup_fail; +} + +void +virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_success; + else + vrtl_eth_dev->dev_ops->tx_queue_setup = + virtual_ethdev_tx_queue_setup_fail; +} + +void +virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success; + else + vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail; +} + + +static uint16_t +virtual_ethdev_rx_burst_success(void *queue __rte_unused, + struct rte_mbuf **bufs, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *pq_map; + struct virtual_ethdev_private *dev_private; + + int i; + + pq_map = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[pq_map->port_id]; + + dev_private = vrtl_eth_dev->data->dev_private; + + if (dev_private->rx_pkt_burst_len > 0) { + if (dev_private->rx_pkt_burst_len < nb_pkts) { + + for (i = 0; i < dev_private->rx_pkt_burst_len; i++) { + bufs[i] = dev_private->rx_pkt_burst[i]; + dev_private->rx_pkt_burst[i] = NULL; + } + + dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len; + } + /* reset private burst values */ + dev_private->rx_pkt_burst_len = 0; + } + + return dev_private->eth_stats.ipackets; +} + +static uint16_t +virtual_ethdev_rx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts __rte_unused) +{ + return 0; +} + +static uint16_t +virtual_ethdev_tx_burst_success(void *queue, + struct rte_mbuf **bufs __rte_unused, + uint16_t nb_pkts) +{ + struct rte_eth_dev *vrtl_eth_dev; + struct virtual_ethdev_queue *tx_q; + struct virtual_ethdev_private *dev_private; + int i; + + tx_q = (struct virtual_ethdev_queue *)queue; + + vrtl_eth_dev = &rte_eth_devices[tx_q->port_id]; + + if (vrtl_eth_dev->data->dev_link.link_status) { + dev_private = vrtl_eth_dev->data->dev_private; + dev_private->eth_stats.opackets += nb_pkts; + + return nb_pkts; + } + + /* free packets in burst */ + for (i = 0; i < nb_pkts; i++) { + if (bufs[i] != NULL) + rte_pktmbuf_free(bufs[i]); + + bufs[i] = NULL; + } + + return 0; +} + + +static uint16_t +virtual_ethdev_tx_burst_fail(void *queue __rte_unused, + struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused) +{ + return 0; +} + + +void +virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + else + vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail; +} + + +void +virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + if (success) + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + else + vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail; +} + + +void +virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status) +{ + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + vrtl_eth_dev->data->dev_link.link_status = link_status; + + _rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC); +} + + + +void +virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, + struct rte_mbuf **pkt_burst, int burst_length) +{ + struct virtual_ethdev_private *dev_private = NULL; + struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id]; + + int i; + + dev_private = vrtl_eth_dev->data->dev_private; + + for (i = 0; i < burst_length; i++) + dev_private->rx_pkt_burst[i] = pkt_burst[i]; + + dev_private->rx_pkt_burst_len = burst_length; +} + +static uint8_t +get_number_of_sockets(void) +{ + int sockets = 0; + int i; + const struct rte_memseg *ms = rte_eal_get_physmem_layout(); + + for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) { + if (sockets < ms[i].socket_id) + sockets = ms[i].socket_id; + } + /* Number of sockets = maximum socket_id + 1 */ + return ++sockets; +} + + +int +virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, + uint8_t socket_id) +{ + struct rte_pci_device *pci_dev = NULL; + struct rte_eth_dev *eth_dev = NULL; + struct eth_driver *eth_drv = NULL; + struct rte_pci_driver *pci_drv = NULL; + struct eth_dev_ops *dev_ops = NULL; + struct rte_pci_id *id_table = NULL; + struct virtual_ethdev_private *dev_private = NULL; + + + /* now do all data allocation - for eth_dev structure, dummy pci driver + * and internal (dev_private) data + */ + + if (socket_id >= get_number_of_sockets()) + goto err; + + pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id); + if (pci_dev == NULL) + goto err; + + eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id); + if (eth_drv == NULL) + goto err; + + pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id); + if (pci_drv == NULL) + goto err; + + dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id); + if (dev_ops == NULL) + goto err; + + id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id); + if (id_table == NULL) + goto err; + + dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id); + if (dev_private == NULL) + goto err; + + /* reserve an ethdev entry */ + eth_dev = rte_eth_dev_allocate(name); + if (eth_dev == NULL) + goto err; + + pci_dev->numa_node = socket_id; + pci_drv->name = virtual_ethdev_driver_name; + pci_drv->id_table = id_table; + + eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv); + eth_dev->driver = eth_drv; + + eth_dev->data->nb_rx_queues = (uint16_t)1; + eth_dev->data->nb_tx_queues = (uint16_t)1; + + TAILQ_INIT(&(eth_dev->callbacks)); + + eth_dev->data->dev_link.link_status = 0; + eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000; + eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX; + + eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0); + if (eth_dev->data->mac_addrs == NULL) + goto err; + + memcpy(eth_dev->data->mac_addrs, mac_addr, + sizeof(*eth_dev->data->mac_addrs)); + eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id; + + eth_dev->data->dev_started = 0; + eth_dev->data->promiscuous = 0; + eth_dev->data->scattered_rx = 0; + eth_dev->data->all_multicast = 0; + + memset(dev_private, 0, sizeof(*dev_private)); + eth_dev->data->dev_private = dev_private; + + eth_dev->dev_ops = dev_ops; + + /* Copy default device operation functions */ + memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops, + sizeof(*eth_dev->dev_ops)); + + eth_dev->pci_dev = pci_dev; + eth_dev->pci_dev->driver = ð_drv->pci_drv; + + eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF; + + eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success; + eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success; + + return eth_dev->data->port_id; + +err: + if (pci_dev) + rte_free(pci_dev); + if (pci_drv) + rte_free(pci_drv); + if (eth_drv) + rte_free(eth_drv); + if (dev_ops) + rte_free(dev_ops); + if (id_table) + rte_free(id_table); + if (dev_private) + rte_free(dev_private); + + return -1; +} diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h new file mode 100644 index 0000000..766b6ac --- /dev/null +++ b/app/test/virtual_pmd.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __VIRTUAL_ETHDEV_H_ +#define __VIRTUAL_ETHDEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_ether.h> + +int virtual_ethdev_init(void); + +int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id); + +void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status); + +void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length); + + +/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */ + +void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIRTUAL_ETHDEV_H_ */ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v4 5/6] testpmd link bonding additions 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (10 preceding siblings ...) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 4/6] Link bonding Unit Tests Declan Doherty @ 2014-06-16 11:18 ` Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 6/6] Link Bonding Library doxygen additions Declan Doherty 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-16 11:18 UTC (permalink / raw) To: dev - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- app/test-pmd/cmdline.c | 571 +++++++++++++++++++++++++++++++++++++++++++++ app/test-pmd/config.c | 4 +- app/test-pmd/parameters.c | 3 + app/test-pmd/testpmd.c | 37 +++- app/test-pmd/testpmd.h | 2 + 5 files changed, 611 insertions(+), 6 deletions(-) diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c index 4678977..fd220e7 100644 --- a/app/test-pmd/cmdline.c +++ b/app/test-pmd/cmdline.c @@ -84,6 +84,9 @@ #include <cmdline_socket.h> #include <cmdline.h> #include <rte_pci_dev_ids.h> +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" @@ -400,6 +403,31 @@ static void cmd_help_long_parsed(void *parsed_result, " Show the bypass configuration for a bypass enabled NIC" " using the lowest port on the NIC.\n\n" #endif +#ifdef RTE_LIBRTE_PMD_BOND + "create bonded device (mode) (socket)\n" + " Create a new bonded device with specific bonding mode and socket.\n\n" + + "add bonding slave (slave_id) (port_id)\n" + " Add a slave device to a bonded device.\n\n" + + "remove bonding slave (slave_id) (port_id)\n" + " Remove a slave device from a bonded device.\n\n" + + "set bonding mode (value) (port_id)\n" + " Set the bonding mode on a bonded device.\n\n" + + "set bonding primary (slave_id) (port_id)\n" + " Set the primary slave for a bonded device.\n\n" + + "show bonding config (port_id)\n" + " Show the bonding config for port_id.\n\n" + + "set bonding mac_addr (port_id) (address)\n" + " Set the MAC address of a bonded device.\n\n" + + "set bonding xmit_balance_policy (port_id) (l2|l23|l34)\n" + " Set the transmit balance policy for bonded device running in balance mode.\n\n" +#endif , list_pkt_forwarding_modes() ); @@ -2856,6 +2884,539 @@ cmdline_parse_inst_t cmd_show_bypass_config = { }; #endif +#ifdef RTE_LIBRTE_PMD_BOND +/* *** SET BONDING MODE *** */ +struct cmd_set_bonding_mode_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mode; + uint8_t value; + uint8_t port_id; +}; + +static void cmd_set_bonding_mode_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_mode_result *res = parsed_result; + portid_t port_id = res->port_id; + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_mode_set(port_id, res->value)) + printf("\t Failed to set bonding mode for port = %d.\n", port_id); +} + +cmdline_parse_token_string_t cmd_setbonding_mode_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_mode_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_mode_mode = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_mode_result, + mode, "mode"); +cmdline_parse_token_num_t cmd_setbonding_mode_value = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + value, UINT8); +cmdline_parse_token_num_t cmd_setbonding_mode_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_mode_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_mode = { + .f = cmd_set_bonding_mode_parsed, + .help_str = "set bonding mode (mode_value) (port_id): Set the bonding mode for port_id", + .data = NULL, + .tokens = { + (void *) &cmd_setbonding_mode_set, + (void *) &cmd_setbonding_mode_bonding, + (void *) &cmd_setbonding_mode_mode, + (void *) &cmd_setbonding_mode_value, + (void *) &cmd_setbonding_mode_port, + NULL + } +}; + +/* *** SET BALANCE XMIT POLICY *** */ +struct cmd_set_bonding_balance_xmit_policy_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t balance_xmit_policy; + uint8_t port_id; + cmdline_fixed_string_t policy; +}; + +static void cmd_set_bonding_balance_xmit_policy_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_balance_xmit_policy_result *res = parsed_result; + portid_t port_id = res->port_id; + uint8_t policy; + + if (!strcmp(res->policy, "l2")) { + policy = BALANCE_XMIT_POLICY_LAYER2; + } else if (!strcmp(res->policy, "l23")) { + policy = BALANCE_XMIT_POLICY_LAYER23; + } else if (!strcmp(res->policy, "l34")) { + policy = BALANCE_XMIT_POLICY_LAYER34; + } else { + printf("\t Invalid xmit policy selection"); + return; + } + + /* Set the bonding mode for the relevant port. */ + if (0 != rte_eth_bond_xmit_policy_set(port_id, policy)) { + printf("\t Failed to set bonding balance xmit policy for port = %d.\n", + port_id); + } +} + +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_balance_xmit_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + balance_xmit_policy, "balance_xmit_policy"); +cmdline_parse_token_num_t cmd_setbonding_balance_xmit_policy_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + port_id, UINT8); +cmdline_parse_token_string_t cmd_setbonding_balance_xmit_policy_policy = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_balance_xmit_policy_result, + policy, "l2#l23#l34"); + +cmdline_parse_inst_t cmd_set_balance_xmit_policy = { + .f = cmd_set_bonding_balance_xmit_policy_parsed, + .help_str = "set bonding balance_xmit_policy (port_id) (policy_value): Set the bonding balance_xmit_policy for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_balance_xmit_policy_set, + (void *)&cmd_setbonding_balance_xmit_policy_bonding, + (void *)&cmd_setbonding_balance_xmit_policy_balance_xmit_policy, + (void *)&cmd_setbonding_balance_xmit_policy_port, + (void *)&cmd_setbonding_balance_xmit_policy_policy, + NULL + } +}; + +/* *** SHOW NIC BONDING CONFIGURATION *** */ +struct cmd_show_bonding_config_result { + cmdline_fixed_string_t show; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t config; + uint8_t port_id; +}; + +static void cmd_show_bonding_config_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_show_bonding_config_result *res = parsed_result; + int bonding_mode; + uint8_t slaves[RTE_MAX_ETHPORTS]; + int num_slaves, num_active_slaves; + int primary_id; + int i; + portid_t port_id = res->port_id; + + /* Display the bonding mode.*/ + bonding_mode = rte_eth_bond_mode_get(port_id); + if (bonding_mode < 0) { + printf("\tFailed to get bonding mode for port = %d\n", port_id); + return; + } else + printf("\tBonding mode: %d\n", bonding_mode); + + if (bonding_mode == BONDING_MODE_BALANCE) { + int balance_xmit_policy; + + balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id); + if (balance_xmit_policy < 0) { + printf("\tFailed to get balance xmit policy for port = %d\n", + port_id); + return; + } else { + printf("\tBalance Xmit Policy: "); + + switch (balance_xmit_policy) { + case BALANCE_XMIT_POLICY_LAYER2: + printf("BALANCE_XMIT_POLICY_LAYER2"); + break; + case BALANCE_XMIT_POLICY_LAYER23: + printf("BALANCE_XMIT_POLICY_LAYER23"); + break; + case BALANCE_XMIT_POLICY_LAYER34: + printf("BALANCE_XMIT_POLICY_LAYER34"); + break; + } + printf("\n"); + } + } + + num_slaves = rte_eth_bond_slaves_get(port_id, slaves, RTE_MAX_ETHPORTS); + + if (num_slaves < 0) { + printf("\tFailed to get slave list for port = %d\n", port_id); + return; + } + if (num_slaves > 0) { + printf("\tSlaves (%d): [", num_slaves); + for (i = 0; i < num_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_slaves - 1]); + } else { + printf("\tSlaves: []\n"); + + } + + num_active_slaves = rte_eth_bond_active_slaves_get(port_id, slaves, + RTE_MAX_ETHPORTS); + + if (num_active_slaves < 0) { + printf("\tFailed to get active slave list for port = %d\n", port_id); + return; + } + if (num_active_slaves > 0) { + printf("\tActive Slaves (%d): [", num_active_slaves); + for (i = 0; i < num_active_slaves - 1; i++) + printf("%d ", slaves[i]); + + printf("%d]\n", slaves[num_active_slaves - 1]); + + } else { + printf("\tActive Slaves: []\n"); + + } + + primary_id = rte_eth_bond_primary_get(port_id); + if (primary_id < 0) { + printf("\tFailed to get primary slave for port = %d\n", port_id); + return; + } else + printf("\tPrimary: [%d]\n", primary_id); + +} + +cmdline_parse_token_string_t cmd_showbonding_config_show = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + show, "show"); +cmdline_parse_token_string_t cmd_showbonding_config_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_showbonding_config_config = +TOKEN_STRING_INITIALIZER(struct cmd_show_bonding_config_result, + config, "config"); +cmdline_parse_token_num_t cmd_showbonding_config_port = +TOKEN_NUM_INITIALIZER(struct cmd_show_bonding_config_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_show_bonding_config = { + .f = cmd_show_bonding_config_parsed, + .help_str = "show bonding config (port_id): Show the bonding config for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_showbonding_config_show, + (void *)&cmd_showbonding_config_bonding, + (void *)&cmd_showbonding_config_config, + (void *)&cmd_showbonding_config_port, + NULL + } +}; + +/* *** SET BONDING PRIMARY *** */ +struct cmd_set_bonding_primary_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t primary; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_set_bonding_primary_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bonding_primary_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_primary_set(master_port_id, slave_port_id)) { + printf("\t Failed to set primary slave for port = %d.\n", + master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_setbonding_primary_set = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + set, "set"); +cmdline_parse_token_string_t cmd_setbonding_primary_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_setbonding_primary_primary = +TOKEN_STRING_INITIALIZER(struct cmd_set_bonding_primary_result, + primary, "primary"); +cmdline_parse_token_num_t cmd_setbonding_primary_slave = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_setbonding_primary_port = +TOKEN_NUM_INITIALIZER(struct cmd_set_bonding_primary_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_set_bonding_primary = { + .f = cmd_set_bonding_primary_parsed, + .help_str = "set bonding primary (slave_id) (port_id): Set the primary slave for port_id", + .data = NULL, + .tokens = { + (void *)&cmd_setbonding_primary_set, + (void *)&cmd_setbonding_primary_bonding, + (void *)&cmd_setbonding_primary_primary, + (void *)&cmd_setbonding_primary_slave, + (void *)&cmd_setbonding_primary_port, + NULL + } +}; + +/* *** ADD SLAVE *** */ +struct cmd_add_bonding_slave_result { + cmdline_fixed_string_t add; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_add_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_add_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_add(master_port_id, slave_port_id)) { + printf("\t Failed to add slave %d to master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_addbonding_slave_add = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + add, "add"); +cmdline_parse_token_string_t cmd_addbonding_slave_bonding = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_addbonding_slave_slave = +TOKEN_STRING_INITIALIZER(struct cmd_add_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_addbonding_slave_slaveid = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_addbonding_slave_port = +TOKEN_NUM_INITIALIZER(struct cmd_add_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_add_bonding_slave = { + .f = cmd_add_bonding_slave_parsed, + .help_str = "add bonding slave (slave_id) (port_id): Add a slave device to a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_addbonding_slave_add, + (void *)&cmd_addbonding_slave_bonding, + (void *)&cmd_addbonding_slave_slave, + (void *)&cmd_addbonding_slave_slaveid, + (void *)&cmd_addbonding_slave_port, + NULL + } +}; + +/* *** REMOVE SLAVE *** */ +struct cmd_remove_bonding_slave_result { + cmdline_fixed_string_t remove; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t slave; + uint8_t slave_id; + uint8_t port_id; +}; + +static void cmd_remove_bonding_slave_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_remove_bonding_slave_result *res = parsed_result; + portid_t master_port_id = res->port_id; + portid_t slave_port_id = res->slave_id; + + /* Set the primary slave for a bonded device. */ + if (0 != rte_eth_bond_slave_remove(master_port_id, slave_port_id)) { + printf("\t Failed to remove slave %d from master port = %d.\n", + slave_port_id, master_port_id); + return; + } + init_port_config(); +} + +cmdline_parse_token_string_t cmd_removebonding_slave_remove = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + remove, "remove"); +cmdline_parse_token_string_t cmd_removebonding_slave_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + bonding, "bonding"); +cmdline_parse_token_string_t cmd_removebonding_slave_slave = + TOKEN_STRING_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave, "slave"); +cmdline_parse_token_num_t cmd_removebonding_slave_slaveid = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + slave_id, UINT8); +cmdline_parse_token_num_t cmd_removebonding_slave_port = + TOKEN_NUM_INITIALIZER(struct cmd_remove_bonding_slave_result, + port_id, UINT8); + +cmdline_parse_inst_t cmd_remove_bonding_slave = { + .f = cmd_remove_bonding_slave_parsed, + .help_str = "remove bonding slave (slave_id) (port_id): Remove a slave device from a bonded device", + .data = NULL, + .tokens = { + (void *)&cmd_removebonding_slave_remove, + (void *)&cmd_removebonding_slave_bonding, + (void *)&cmd_removebonding_slave_slave, + (void *)&cmd_removebonding_slave_slaveid, + (void *)&cmd_removebonding_slave_port, + NULL + } +}; + +/* *** CREATE BONDED DEVICE *** */ +struct cmd_create_bonded_device_result { + cmdline_fixed_string_t create; + cmdline_fixed_string_t bonded; + cmdline_fixed_string_t device; + uint8_t mode; + uint8_t socket; +}; + +static void cmd_create_bonded_device_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_create_bonded_device_result *res = parsed_result; + + int port_id; + if (test_done == 0) { + printf("Please stop forwarding first\n"); + return; + } + + port_id = rte_eth_bond_create("testpmd-bonded-dev", res->mode, res->socket); + /* Create a new bonded device. */ + if (port_id < 0) { + printf("\t Failed to create bonded device.\n"); + return; + } else { + printf("\t Created new bonded device (port %d).\n", port_id); + /* Update number of ports */ + nb_ports = rte_eth_dev_count(); + reconfig(port_id); + rte_eth_promiscuous_enable(port_id); + } + +} + +cmdline_parse_token_string_t cmd_createbonded_device_create = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + create, "create"); +cmdline_parse_token_string_t cmd_createbonded_device_bonded = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + bonded, "bonded"); +cmdline_parse_token_string_t cmd_createbonded_device_device = + TOKEN_STRING_INITIALIZER(struct cmd_create_bonded_device_result, + device, "device"); +cmdline_parse_token_num_t cmd_createbonded_device_mode = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + mode, UINT8); +cmdline_parse_token_num_t cmd_createbonded_device_socket = + TOKEN_NUM_INITIALIZER(struct cmd_create_bonded_device_result, + socket, UINT8); + +cmdline_parse_inst_t cmd_create_bonded_device = { + .f = cmd_create_bonded_device_parsed, + .help_str = "create bonded device (mode) (socket): Create a new bonded device with specific bonding mode and socket", + .data = NULL, + .tokens = { + (void *)&cmd_createbonded_device_create, + (void *)&cmd_createbonded_device_bonded, + (void *)&cmd_createbonded_device_device, + (void *)&cmd_createbonded_device_mode, + (void *)&cmd_createbonded_device_socket, + NULL + } +}; + +/* *** SET MAC ADDRESS IN BONDED DEVICE *** */ +struct cmd_set_bond_mac_addr_result { + cmdline_fixed_string_t set; + cmdline_fixed_string_t bonding; + cmdline_fixed_string_t mac_addr; + uint8_t port_num; + struct ether_addr address; +}; + +static void cmd_set_bond_mac_addr_parsed(void *parsed_result, + __attribute__((unused)) struct cmdline *cl, + __attribute__((unused)) void *data) +{ + struct cmd_set_bond_mac_addr_result *res = parsed_result; + int ret; + + if (res->port_num >= nb_ports) { + printf("Port id %d must be less than %d\n", res->port_num, nb_ports); + return; + } + + ret = rte_eth_bond_mac_address_set(res->port_num, &res->address); + + /* check the return value and print it if is < 0 */ + if (ret < 0) + printf("set_bond_mac_addr error: (%s)\n", strerror(-ret)); +} + +cmdline_parse_token_string_t cmd_set_bond_mac_addr_set = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, set, "set"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_bonding = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, bonding, + "bonding"); +cmdline_parse_token_string_t cmd_set_bond_mac_addr_mac = + TOKEN_STRING_INITIALIZER(struct cmd_set_bond_mac_addr_result, mac_addr, + "mac_addr"); +cmdline_parse_token_num_t cmd_set_bond_mac_addr_portnum = + TOKEN_NUM_INITIALIZER(struct cmd_set_bond_mac_addr_result, port_num, UINT8); +cmdline_parse_token_etheraddr_t cmd_set_bond_mac_addr_addr = + TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_bond_mac_addr_result, address); + +cmdline_parse_inst_t cmd_set_bond_mac_addr = { + .f = cmd_set_bond_mac_addr_parsed, + .data = (void *) 0, + .help_str = "set bonding mac_addr (port_id) (address): ", + .tokens = { + (void *)&cmd_set_bond_mac_addr_set, + (void *)&cmd_set_bond_mac_addr_bonding, + (void *)&cmd_set_bond_mac_addr_mac, + (void *)&cmd_set_bond_mac_addr_portnum, + (void *)&cmd_set_bond_mac_addr_addr, + NULL + } +}; + +#endif /* RTE_LIBRTE_PMD_BOND */ + /* *** SET FORWARDING MODE *** */ struct cmd_set_fwd_mode_result { cmdline_fixed_string_t set; @@ -5567,6 +6128,16 @@ cmdline_parse_ctx_t main_ctx[] = { (cmdline_parse_inst_t *)&cmd_set_bypass_timeout, (cmdline_parse_inst_t *)&cmd_show_bypass_config, #endif +#ifdef RTE_LIBRTE_PMD_BOND + (cmdline_parse_inst_t *) &cmd_set_bonding_mode, + (cmdline_parse_inst_t *) &cmd_show_bonding_config, + (cmdline_parse_inst_t *) &cmd_set_bonding_primary, + (cmdline_parse_inst_t *) &cmd_add_bonding_slave, + (cmdline_parse_inst_t *) &cmd_remove_bonding_slave, + (cmdline_parse_inst_t *) &cmd_create_bonded_device, + (cmdline_parse_inst_t *) &cmd_set_bond_mac_addr, + (cmdline_parse_inst_t *) &cmd_set_balance_xmit_policy, +#endif (cmdline_parse_inst_t *)&cmd_vlan_offload, (cmdline_parse_inst_t *)&cmd_vlan_tpid, (cmdline_parse_inst_t *)&cmd_rx_vlan_filter_all, diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 52ad01a..d1ec1e5 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -242,6 +242,7 @@ void port_infos_display(portid_t port_id) { struct rte_port *port; + struct ether_addr mac_addr; struct rte_eth_link link; int vlan_offload; struct rte_mempool * mp; @@ -255,7 +256,8 @@ port_infos_display(portid_t port_id) rte_eth_link_get_nowait(port_id, &link); printf("\n%s Infos for port %-2d %s\n", info_border, port_id, info_border); - print_ethaddr("MAC address: ", &port->eth_addr); + rte_eth_macaddr_get(port_id, &mac_addr); + print_ethaddr("MAC address: ", &mac_addr); printf("\nConnect to socket: %u", port->socket_id); if (port_numa[port_id] != NUMA_NO_CONFIG) { diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index eb14cd3..63669f5 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -75,6 +75,9 @@ #include <cmdline_parse.h> #include <cmdline_parse_etheraddr.h> #endif +#ifdef RTE_LIBRTE_PMD_BOND +#include <rte_eth_bond.h> +#endif #include "testpmd.h" diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index 2529dc3..e3de4ad 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -300,7 +300,7 @@ struct rte_fdir_conf fdir_conf = { .drop_queue = 127, }; -static volatile int test_done = 1; /* stop packet forwarding when set to 1. */ +volatile int test_done = 1; /* stop packet forwarding when set to 1. */ struct queue_stats_mappings tx_queue_stats_mappings_array[MAX_TX_QUEUE_STATS_MAPPINGS]; struct queue_stats_mappings rx_queue_stats_mappings_array[MAX_RX_QUEUE_STATS_MAPPINGS]; @@ -625,6 +625,32 @@ init_config(void) rte_exit(EXIT_FAILURE, "FAIL from init_fwd_streams()\n"); } + +void +reconfig(portid_t new_port_id) +{ + struct rte_port *port; + + /* Reconfiguration of Ethernet ports. */ + ports = rte_realloc(ports, + sizeof(struct rte_port) * nb_ports, + CACHE_LINE_SIZE); + if (ports == NULL) { + rte_exit(EXIT_FAILURE, "rte_realloc(%d struct rte_port) failed\n", + nb_ports); + } + + port = &ports[new_port_id]; + rte_eth_dev_info_get(new_port_id, &port->dev_info); + + /* set flag to initialize port/queue */ + port->need_reconfig = 1; + port->need_reconfig_queues = 1; + + init_port_config(); +} + + int init_fwd_streams(void) { @@ -1246,7 +1272,7 @@ start_port(portid_t pid) portid_t pi; queueid_t qi; struct rte_port *port; - uint8_t *mac_addr; + struct ether_addr mac_addr; if (test_done == 0) { printf("Please stop forwarding first\n"); @@ -1374,10 +1400,11 @@ start_port(portid_t pid) RTE_PORT_HANDLING, RTE_PORT_STARTED) == 0) printf("Port %d can not be set into started\n", pi); - mac_addr = port->eth_addr.addr_bytes; + rte_eth_macaddr_get(pi, &mac_addr); printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", pi, - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); + mac_addr.addr_bytes[0], mac_addr.addr_bytes[1], + mac_addr.addr_bytes[2], mac_addr.addr_bytes[3], + mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]); /* at least one port started, need checking link status */ need_check_link_status = 1; diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 4fabf1c..892d39f 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -279,6 +279,7 @@ extern uint16_t port_topology; /**< set by "--port-topology" parameter */ extern uint8_t no_flush_rx; /**<set by "--no-flush-rx" parameter */ extern uint8_t mp_anon; /**< set by "--mp-anon" parameter */ extern uint8_t no_link_check; /**<set by "--disable-link-check" parameter */ +extern volatile int test_done; /* stop packet forwarding when set to 1. */ #ifdef RTE_NIC_BYPASS extern uint32_t bypass_timeout; /**< Store the NIC bypass watchdog timeout */ @@ -452,6 +453,7 @@ void fwd_config_display(void); void rxtx_config_display(void); void fwd_config_setup(void); void set_def_fwd_config(void); +void reconfig(portid_t new_port_id); int init_fwd_streams(void); -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
* [dpdk-dev] [PATCH v4 6/6] Link Bonding Library doxygen additions 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty ` (11 preceding siblings ...) 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 5/6] testpmd link bonding additions Declan Doherty @ 2014-06-16 11:18 ` Declan Doherty 12 siblings, 0 replies; 127+ messages in thread From: Declan Doherty @ 2014-06-16 11:18 UTC (permalink / raw) To: dev Signed-off-by: Declan Doherty <declan.doherty@intel.com> --- doc/doxy-api-index.md | 1 + doc/doxy-api.conf | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diff --git a/doc/doxy-api-index.md b/doc/doxy-api-index.md index 83303a1..8338a9b 100644 --- a/doc/doxy-api-index.md +++ b/doc/doxy-api-index.md @@ -36,6 +36,7 @@ API {#index} There are many libraries, so their headers may be grouped by topics: - **device**: + [bond] (@ref rte_eth_bond.h), [ethdev] (@ref rte_ethdev.h), [devargs] (@ref rte_devargs.h), [KNI] (@ref rte_kni.h), diff --git a/doc/doxy-api.conf b/doc/doxy-api.conf index e5a8520..3af8dad 100644 --- a/doc/doxy-api.conf +++ b/doc/doxy-api.conf @@ -30,6 +30,7 @@ PROJECT_NAME = DPDK INPUT = doc/doxy-api-index.md \ + lib/librte_pmd_bond \ lib/librte_eal/common/include \ lib/librte_acl \ lib/librte_distributor \ -- 1.7.0.7 ^ permalink raw reply [flat|nested] 127+ messages in thread
end of thread, other threads:[~2014-07-01 22:01 UTC | newest] Thread overview: 127+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 1/4] " declan.doherty 2014-05-28 16:54 ` Shaw, Jeffrey B 2014-05-29 13:32 ` Doherty, Declan 2014-05-28 15:32 ` [dpdk-dev] [PATCH 2/4] Link bonding unit tests declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 3/4] Link bonding integration into testpmd declan.doherty 2014-05-28 15:32 ` [dpdk-dev] [PATCH 4/4] Add Link Bonding Library to Doxygen declan.doherty 2014-05-28 17:49 ` [dpdk-dev] [PATCH 0/4] Link Bonding Library Neil Horman 2014-05-29 10:33 ` Doherty, Declan 2014-05-29 11:33 ` Neil Horman 2014-05-29 3:23 ` Cao, Waterman 2014-05-29 10:35 ` Doherty, Declan 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty 2014-06-04 15:18 ` declan.doherty 2014-06-05 15:15 ` Stephen Hemminger 2014-06-06 9:07 ` Doherty, Declan 2014-06-06 15:13 ` Stephen Hemminger 2014-06-09 21:11 ` Eric Kinzie 2014-06-13 14:03 ` Doherty, Declan 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 2/4] Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing - checkpack fixes declan.doherty 2014-06-04 15:18 ` declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 3/4] Adding link bonding support to testpmd. - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices declan.doherty 2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 4/4] Add Link Bonding Library to Doxygen declan.doherty 2014-06-04 16:10 ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library Doherty, Declan 2014-06-05 8:03 ` De Lara Guarch, Pablo 2014-06-05 11:03 ` Neil Horman 2014-06-06 8:23 ` Doherty, Declan 2014-06-06 14:54 ` Neil Horman 2014-06-13 14:56 ` Doherty, Declan 2014-06-13 15:11 ` Neil Horman 2014-06-06 3:26 ` Cao, Waterman 2014-06-11 16:33 ` Thomas Monjalon 2014-06-13 14:08 ` Doherty, Declan 2014-06-13 15:15 ` Thomas Monjalon 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 1/5] " Declan Doherty 2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) Declan Doherty 2014-06-13 16:08 ` Neil Horman 2014-06-13 18:34 ` Doherty, Declan 2014-06-13 19:38 ` Neil Horman 2014-06-16 8:59 ` Doherty, Declan 2014-06-16 11:07 ` Neil Horman 2014-06-16 16:17 ` Richardson, Bruce 2014-06-16 17:47 ` Neil Horman 2014-06-16 18:07 ` Richardson, Bruce 2014-06-16 18:09 ` Thomas Monjalon 2014-06-13 21:59 ` Stephen Hemminger 2014-06-16 7:59 ` Doherty, Declan 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 3/5] Link Bonding PMD Library (Unit Test Suite) Declan Doherty 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 4/5] Link Bonding PMD Library (testpmd link bonding API support) Declan Doherty 2014-06-13 14:42 ` [dpdk-dev] [PATCH v3 5/5] Link Bonding PMD Library (Doxygen Additions) Declan Doherty 2014-06-13 15:20 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Neil Horman 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 " Declan Doherty 2014-06-18 16:18 ` Neil Horman 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 " Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 " Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 " Declan Doherty 2014-06-26 16:02 ` De Lara Guarch, Pablo 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library Thomas Monjalon 2014-06-27 0:45 ` Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 2/5] ethdev: add unique name to devices Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 3/5] eal: support link bonding device initialization Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 4/5] bond: unit tests Thomas Monjalon 2014-06-26 23:57 ` [dpdk-dev] [PATCH v9 5/5] bond: testpmd support Thomas Monjalon 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty 2014-06-27 20:58 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 0/5] link bonding library Declan Doherty 2014-06-30 9:21 ` Thomas Monjalon 2014-06-30 9:28 ` Doherty, Declan 2014-07-01 22:01 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 1/5] bond: new " Declan Doherty 2014-06-30 9:13 ` Thomas Monjalon 2014-06-30 22:29 ` Robert Sanford 2014-07-01 14:16 ` Thomas Monjalon 2014-07-01 14:19 ` Doherty, Declan 2014-07-01 14:26 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 2/5] ethdev: add unique name to devices Declan Doherty 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 3/5] eal: support link bonding device initialization Declan Doherty 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 4/5] bond: unit tests Declan Doherty 2014-06-30 8:56 ` Thomas Monjalon 2014-06-29 17:49 ` [dpdk-dev] [PATCH v11 5/5] bond: testpmd support Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 1/5] bond: new link bonding library Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 2/5] ethdev: add unique name to devices Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 3/5] eal: support link bonding device initialization Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 4/5] bond: unit tests Declan Doherty 2014-06-27 10:18 ` [dpdk-dev] [PATCH v10 5/5] bond: testpmd support Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 2/6] Support for unique interface naming of pmds Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 3/6] EAL support for link bonding device initialization Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 4/6] Link bonding Unit Tests Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 5/6] testpmd link bonding additions Declan Doherty 2014-06-25 20:07 ` [dpdk-dev] [PATCH v8 6/6] Link Bonding Library doxygen additions Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 2/6] Support for unique interface naming of pmds Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization Declan Doherty 2014-06-25 13:54 ` Thomas Monjalon 2014-06-25 14:41 ` Doherty, Declan 2014-06-25 16:00 ` Thomas Monjalon 2014-06-25 16:15 ` Richardson, Bruce 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 4/6] Link bonding Unit Tests Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 5/6] testpmd link bonding additions Declan Doherty 2014-06-24 16:03 ` [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions Declan Doherty 2014-06-25 13:43 ` Thomas Monjalon 2014-06-25 14:19 ` Doherty, Declan 2014-06-25 14:23 ` Thomas Monjalon 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 2/6] Support for unique interface naming of pmds Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 3/6] EAL support for link bonding device initialization Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 4/6] Link bonding Unit Tests Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 5/6] testpmd link bonding additions Declan Doherty 2014-06-24 14:52 ` [dpdk-dev] [PATCH v6 6/6] Link Bonding Library doxygen additions Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 2/6] Support for unique interface naming of pmds Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 3/6] EAL support for link bonding device initialization Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 4/6] Link bonding Unit Tests Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 5/6] testpmd link bonding additions Declan Doherty 2014-06-18 16:14 ` [dpdk-dev] [PATCH v5 6/6] Link Bonding Library doxygen additions Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 1/6] Link Bonding Library (lib/librte_pmd_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, la Mode 3 - Broadcast Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 2/6] Support for unique interface naming of pmds Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 3/6] EAL support for link bonding device initialization Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 4/6] Link bonding Unit Tests Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 5/6] testpmd link bonding additions Declan Doherty 2014-06-16 11:18 ` [dpdk-dev] [PATCH v4 6/6] Link Bonding Library doxygen additions Declan Doherty
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).