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_burst_round_robin;
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst;
break;
case BONDING_MODE_ACTIVE_BACKUP:
eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_active_backup;
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst_active_backup;
break;
case BONDING_MODE_BALANCE:
eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_balance;
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst;
break;
case BONDING_MODE_BROADCAST:
eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_broadcast;
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst;
break;
case BONDING_MODE_8023AD:
if (bond_mode_8023ad_enable(eth_dev) != 0)
return -1;
if (internals->mode4.dedicated_queues.enabled == 0) {
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst_8023ad;
eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_8023ad;
RTE_BOND_LOG(WARNING,
"Using mode 4, it is necessary to do TX burst "
"and RX burst at least every 100ms.");
} else {
/* Use flow director's optimization */
eth_dev->rx_pkt_burst =
bond_ethdev_rx_burst_8023ad_fast_queue;
eth_dev->tx_pkt_burst =
bond_ethdev_tx_burst_8023ad_fast_queue;
}
break;
case BONDING_MODE_TLB:
eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_tlb;
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst_active_backup;
break;
case BONDING_MODE_ALB:
if (bond_mode_alb_enable(eth_dev) != 0)
return -1;
eth_dev->tx_pkt_burst = bond_ethdev_tx_burst_alb;
eth_dev->rx_pkt_burst = bond_ethdev_rx_burst_alb;
break;
default:
return -1;
}
internals->mode = mode;
return 0;
}
bond_ethdev_rx_queue_setup
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->data->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;
}
bond_ethdev_rx_burst
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_total = 0;
uint16_t slave_count;
uint16_t active_slave;
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;
slave_count = internals->active_slave_count;
active_slave = internals->active_slave;
for (i = 0; i < slave_count && nb_pkts; i++) {
uint16_t num_rx_slave;
/* Offset of pointer to *bufs increases as packets are received
* from other slaves */
num_rx_slave =
rte_eth_rx_burst(internals->active_slaves[active_slave],
bd_rx_q->queue_id,
bufs + num_rx_total, nb_pkts);
num_rx_total += num_rx_slave;
nb_pkts -= num_rx_slave;
if (++active_slave == slave_count)
active_slave = 0;
}
if (++internals->active_slave >= slave_count)
internals->active_slave = 0;
return num_rx_total;
}
rte_eth_bond_slave_add
rte_eth_bond_slave_add(uint16_t bonded_port_id, uint16_t slave_port_id)
{
struct rte_eth_dev *bonded_eth_dev;
struct bond_dev_private *internals;
int retval;
/* Verify that port id's are valid bonded and slave ports */
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;
rte_spinlock_lock(&internals->lock);
retval = __eth_bond_slave_add_lock_free(bonded_port_id, slave_port_id);
rte_spinlock_unlock(&internals->lock);
return retval;
}
static int
__eth_bond_slave_add_lock_free(uint16_t bonded_port_id, uint16_t slave_port_id)
{
struct rte_eth_dev *bonded_eth_dev, *slave_eth_dev;
struct bond_dev_private *internals;
struct rte_eth_link link_props;
struct rte_eth_dev_info dev_info;
int ret;
bonded_eth_dev = &rte_eth_devices[bonded_port_id];
internals = bonded_eth_dev->data->dev_private;
if (valid_slave_port_id(slave_port_id, internals->mode) != 0)
return -1;
slave_eth_dev = &rte_eth_devices[slave_port_id];
if (slave_eth_dev->data->dev_flags & RTE_ETH_DEV_BONDED_SLAVE) {
RTE_BOND_LOG(ERR, "Slave device is already a slave of a bonded device");
return -1;
}
ret = rte_eth_dev_info_get(slave_port_id, &dev_info);
if (ret != 0) {
RTE_BOND_LOG(ERR,
"%s: Error during getting device (port %u) info: %s
",
__func__, slave_port_id, strerror(-ret));
return ret;
}
if (dev_info.max_rx_pktlen < internals->max_rx_pktlen) {
RTE_BOND_LOG(ERR, "Slave (port %u) max_rx_pktlen too small",
slave_port_id);
return -1;
}
slave_add(internals, slave_eth_dev);
/* We need to store slaves reta_size to be able to synchronize RETA for all
* slave devices even if its sizes are different.
*/
internals->slaves[internals->slave_count].reta_size = dev_info.reta_size;
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) {
if (mac_address_set(bonded_eth_dev,
slave_eth_dev->data->mac_addrs)) {
RTE_BOND_LOG(ERR, "Failed to set MAC address");
return -1;
}
}
/* Make primary slave */
internals->primary_port = slave_port_id;
internals->current_primary_port = slave_port_id;
/* Inherit queues settings from first slave */
internals->nb_rx_queues = slave_eth_dev->data->nb_rx_queues;
internals->nb_tx_queues = slave_eth_dev->data->nb_tx_queues;
eth_bond_slave_inherit_dev_info_rx_first(internals, &dev_info);
eth_bond_slave_inherit_dev_info_tx_first(internals, &dev_info);
eth_bond_slave_inherit_desc_lim_first(&internals->rx_desc_lim,
&dev_info.rx_desc_lim);
eth_bond_slave_inherit_desc_lim_first(&internals->tx_desc_lim,
&dev_info.tx_desc_lim);
} else {
int ret;
eth_bond_slave_inherit_dev_info_rx_next(internals, &dev_info);
eth_bond_slave_inherit_dev_info_tx_next(internals, &dev_info);
ret = eth_bond_slave_inherit_desc_lim_next(
&internals->rx_desc_lim, &dev_info.rx_desc_lim);
if (ret != 0)
return ret;
ret = eth_bond_slave_inherit_desc_lim_next(
&internals->tx_desc_lim, &dev_info.tx_desc_lim);
if (ret != 0)
return ret;
}
bonded_eth_dev->data->dev_conf.rx_adv_conf.rss_conf.rss_hf &=
internals->flow_type_rss_offloads;
if (slave_rte_flow_prepare(internals->slave_count, internals) != 0) {
RTE_BOND_LOG(ERR, "Failed to prepare new slave flows: port=%d",
slave_port_id);
return -1;
}
/* Add additional MAC addresses to the slave */
if (slave_add_mac_addresses(bonded_eth_dev, slave_port_id) != 0) {
RTE_BOND_LOG(ERR, "Failed to add mac address(es) to slave %hu",
slave_port_id);
return -1;
}
internals->slave_count++;
if (bonded_eth_dev->data->dev_started) {
if (slave_configure(bonded_eth_dev, slave_eth_dev) != 0) {
internals->slave_count--;
RTE_BOND_LOG(ERR, "rte_bond_slaves_configure: port=%d",
slave_port_id);
return -1;
}
}
/* Update all slave devices MACs */
mac_address_slaves_update(bonded_eth_dev);
/* 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) {
ret = rte_eth_link_get_nowait(slave_port_id, &link_props);
if (ret < 0) {
rte_eth_dev_callback_unregister(slave_port_id,
RTE_ETH_EVENT_INTR_LSC,
/* If bonded device is started then we can add the slave to our active
* slave array */
if (bonded_eth_dev->data->dev_started) {
ret = rte_eth_link_get_nowait(slave_port_id, &link_props);
if (ret < 0) {
rte_eth_dev_callback_unregister(slave_port_id,
RTE_ETH_EVENT_INTR_LSC,
bond_ethdev_lsc_event_callback,
&bonded_eth_dev->data->port_id);
internals->slave_count--;
RTE_BOND_LOG(ERR,
"Slave (port %u) link get failed: %s
",
slave_port_id, rte_strerror(-ret));
return -1;
}
if (link_props.link_status == ETH_LINK_UP) {
if (internals->active_slave_count == 0 &&
!internals->user_defined_primary_port)
bond_ethdev_primary_set(internals,
slave_port_id);
}
}
/* Add slave details to bonded device */
slave_eth_dev->data->dev_flags |= RTE_ETH_DEV_BONDED_SLAVE;
slave_vlan_filter_set(bonded_port_id, slave_port_id);
return 0;
}
eth_bond_slave_inherit_dev_info_rx_next
static void
eth_bond_slave_inherit_dev_info_rx_next(struct bond_dev_private *internals,
const struct rte_eth_dev_info *di)
{
struct rte_eth_rxconf *rxconf_i = &internals->default_rxconf;
const struct rte_eth_rxconf *rxconf = &di->default_rxconf;
internals->rx_offload_capa &= di->rx_offload_capa;
internals->rx_queue_offload_capa &= di->rx_queue_offload_capa;
internals->flow_type_rss_offloads &= di->flow_type_rss_offloads;
/*
* If at least one slave device suggests enabling this
* setting by default, enable it for all slave devices
* since disabling it may not be necessarily supported.
*/
if (rxconf->rx_drop_en == 1)
rxconf_i->rx_drop_en = 1;
/*
* Adding a new slave device may cause some of previously inherited
* offloads to be withdrawn from the internal rx_queue_offload_capa
* value. Thus, the new internal value of default Rx queue offloads
* has to be masked by rx_queue_offload_capa to make sure that only
* commonly supported offloads are preserved from both the previous
* value and the value being inhereted from the new slave device.
*/
rxconf_i->offloads = (rxconf_i->offloads | rxconf->offloads) &
internals->rx_queue_offload_capa;
/*
* RETA size is GCD of all slaves RETA sizes, so, if all sizes will be
* the power of 2, the lower one is GCD
*/
if (internals->reta_size > di->reta_size)
internals->reta_size = di->reta_size;
if (!internals->max_rx_pktlen &&
di->max_rx_pktlen < internals->candidate_max_rx_pktlen)
internals->candidate_max_rx_pktlen = di->max_rx_pktlen;
}
primary
bond_ethdev_primary_set(struct bond_dev_private *internals,
uint16_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;
}
}
bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev)
{
struct bond_dev_private *internals = eth_dev->data->dev_private;
int i;
int ret = 0;
uint16_t port_id;
switch (internals->mode) {
/* Promiscuous mode is propagated to all slaves */
case BONDING_MODE_ROUND_ROBIN:
case BONDING_MODE_BALANCE:
case BONDING_MODE_BROADCAST:
case BONDING_MODE_8023AD: {
unsigned int slave_ok = 0;
for (i = 0; i < internals->slave_count; i++) {
port_id = internals->slaves[i].port_id;
ret = rte_eth_promiscuous_enable(port_id);
if (ret != 0)
RTE_BOND_LOG(ERR,
"Failed to enable promiscuous mode for port %u: %s",
port_id, rte_strerror(-ret));
else
slave_ok++;
}
/*
* Report success if operation is successful on at least
* on one slave. Otherwise return last error code.
*/
if (slave_ok > 0)
ret = 0;
break;
}
/* Promiscuous mode is propagated only to primary slave */
case BONDING_MODE_ACTIVE_BACKUP:
case BONDING_MODE_TLB:
case BONDING_MODE_ALB:
default:
/* Do not touch promisc when there cannot be primary ports */
if (internals->slave_count == 0)
break;
port_id = internals->current_primary_port;
ret = rte_eth_promiscuous_enable(port_id);
if (ret != 0)
RTE_BOND_LOG(ERR,
"Failed to enable promiscuous mode for port %u: %s",
port_id, rte_strerror(-ret));
}
return ret;
}