Packet forwarding to/from bond interfaces is done in software. This patch enables certain platforms to bridge traffic to/from bond interfaces in hardware. Notifications are sent out when the "active" slave set for a bond interface is updated in software. Platforms use the notifications to program the hardware accordingly. The changes have been verified to work with configured and 802.3ad bond interfaces.
Signed-off-by: Premkumar Jonnala <pjonn...@broadcom.com> --- diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b4351ca..4b53733 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3759,6 +3759,101 @@ err: bond_slave_arr_work_rearm(bond, 1); } +static int slave_present(struct slave *slave, struct bond_up_slave *arr) +{ + int i; + + if (!arr) + return 0; + + for (i = 0; i < arr->count; i++) { + if (arr->arr[i] == slave) + return 1; + } + return 0; +} + +/* Send notification to clear/remove slaves for 'bond' in 'arr' except for + * slaves in 'ignore_arr'. + */ +static int bond_slave_arr_clear_notify(struct bonding *bond, + struct bond_up_slave *arr, + struct bond_up_slave *ignore_arr) +{ + struct slave *slave; + struct net_device *slave_dev; + int i, rv; + const struct net_device_ops *ops; + + if (!bond->dev || !arr) + return -EINVAL; + + rv = 0; + for (i = 0; i < arr->count; i++) { + slave = arr->arr[i]; + if (!slave || !slave->dev) + continue; + + slave_dev = slave->dev; + if (slave_present(slave, ignore_arr)) { + netdev_dbg(bond->dev, "ignoring clear of slave %s\n", + slave_dev->name); + continue; + } + ops = slave_dev->netdev_ops; + if (!ops || !ops->ndo_bond_slave_discard) { + netdev_dbg(bond->dev, "No slave discard ops for %s\n", + slave_dev->name); + continue; + } + rv = ops->ndo_bond_slave_discard(slave_dev, bond->dev); + if (rv < 0) + return rv; + } + return rv; +} + +/* Send notification about updated slaves for 'bond' except for slaves in + * 'ignore_arr'. + */ +static int bond_slave_arr_set_notify(struct bonding *bond, + struct bond_up_slave *ignore_arr) +{ + struct slave *slave; + struct net_device *slave_dev; + struct bond_up_slave *arr; + int i, rv; + const struct net_device_ops *ops; + + if (!bond || !bond->dev) + return -EINVAL; + rv = 0; + + arr = rtnl_dereference(bond->slave_arr); + if (!arr) + return -EINVAL; + + for (i = 0; i < arr->count; i++) { + slave = arr->arr[i]; + slave_dev = slave->dev; + if (slave_present(slave, ignore_arr)) { + netdev_dbg(bond->dev, "ignoring add of slave %s\n", + slave->dev->name); + continue; + } + ops = slave_dev->netdev_ops; + if (!ops || !ops->ndo_bond_slave_add) { + netdev_dbg(bond->dev, "No slave add ops for %s\n", + slave_dev->name); + continue; + } + rv = ops->ndo_bond_slave_add(slave_dev, bond->dev); + if (rv < 0) + return rv; + } + return rv; +} + /* Build the usable slaves array in control path for modes that use xmit-hash * to determine the slave interface - * (a) BOND_MODE_8023AD @@ -3771,7 +3866,7 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) { struct slave *slave; struct list_head *iter; - struct bond_up_slave *new_arr, *old_arr; + struct bond_up_slave *new_arr, *old_arr, *discard_arr = 0; int agg_id = 0; int ret = 0; @@ -3786,6 +3881,12 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) pr_err("Failed to build slave-array.\n"); goto out; } + discard_arr = kzalloc(offsetof(struct bond_up_slave, arr[bond->slave_cnt]), + GFP_KERNEL); + if (!discard_arr) { + ret = -ENOMEM; + goto out; + } if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct ad_info ad_info; @@ -3797,6 +3898,7 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) */ old_arr = rtnl_dereference(bond->slave_arr); if (old_arr) { + bond_slave_arr_clear_notify(bond, old_arr, 0); RCU_INIT_POINTER(bond->slave_arr, NULL); kfree_rcu(old_arr, rcu); } @@ -3809,8 +3911,10 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) struct aggregator *agg; agg = SLAVE_AD_INFO(slave)->port.aggregator; - if (!agg || agg->aggregator_identifier != agg_id) + if (!agg || agg->aggregator_identifier != agg_id) { + discard_arr->arr[discard_arr->count++] = slave; continue; + } } if (!bond_slave_can_tx(slave)) continue; @@ -3820,10 +3924,15 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) } old_arr = rtnl_dereference(bond->slave_arr); + bond_slave_arr_clear_notify(bond, old_arr, new_arr); + bond_slave_arr_clear_notify(bond, discard_arr, 0); rcu_assign_pointer(bond->slave_arr, new_arr); + bond_slave_arr_set_notify(bond, old_arr); if (old_arr) kfree_rcu(old_arr, rcu); out: + if (discard_arr) + kfree(discard_arr); if (ret != 0 && skipslave) { int idx; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4ac653b..facc35f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1236,6 +1236,10 @@ struct net_device_ops { bool proto_down); int (*ndo_fill_metadata_dst)(struct net_device *dev, struct sk_buff *skb); + int (*ndo_bond_slave_add)(struct net_device *slave_dev, + struct net_device *bond); + int (*ndo_bond_slave_discard)(struct net_device *slave_dev, + struct net_device *bond); }; /** -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html