diff options
Diffstat (limited to 'drivers/net/bonding/bond_alb.c')
-rw-r--r-- | drivers/net/bonding/bond_alb.c | 55 |
1 files changed, 50 insertions, 5 deletions
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index f16442fa97ff..d3c6801f101e 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -200,6 +200,7 @@ static int tlb_initialize(struct bonding *bond) static void tlb_deinitialize(struct bonding *bond) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct tlb_up_slave *arr; _lock_tx_hashtbl_bh(bond); @@ -207,6 +208,10 @@ static void tlb_deinitialize(struct bonding *bond) bond_info->tx_hashtbl = NULL; _unlock_tx_hashtbl_bh(bond); + + arr = rtnl_dereference(bond_info->slave_arr); + if (arr) + kfree_rcu(arr, rcu); } static long long compute_gap(struct slave *slave) @@ -1402,9 +1407,39 @@ out: return NETDEV_TX_OK; } +static int bond_tlb_update_slave_arr(struct bonding *bond, + struct slave *skipslave) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct slave *tx_slave; + struct list_head *iter; + struct tlb_up_slave *new_arr, *old_arr; + + new_arr = kzalloc(offsetof(struct tlb_up_slave, arr[bond->slave_cnt]), + GFP_ATOMIC); + if (!new_arr) + return -ENOMEM; + + bond_for_each_slave(bond, tx_slave, iter) { + if (!bond_slave_can_tx(tx_slave)) + continue; + if (skipslave == tx_slave) + continue; + new_arr->arr[new_arr->count++] = tx_slave; + } + + old_arr = rtnl_dereference(bond_info->slave_arr); + rcu_assign_pointer(bond_info->slave_arr, new_arr); + if (old_arr) + kfree_rcu(old_arr, rcu); + + return 0; +} + int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct ethhdr *eth_data; struct slave *tx_slave = NULL; u32 hash_index; @@ -1425,12 +1460,12 @@ int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev) hash_index & 0xFF, skb->len); } else { - struct list_head *iter; - int idx = hash_index % bond->slave_cnt; + struct tlb_up_slave *slaves; - bond_for_each_slave_rcu(bond, tx_slave, iter) - if (--idx < 0) - break; + slaves = rcu_dereference(bond_info->slave_arr); + if (slaves && slaves->count) + tx_slave = slaves->arr[hash_index % + slaves->count]; } break; } @@ -1695,6 +1730,11 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave) bond->alb_info.rx_slave = NULL; rlb_clear_slave(bond, slave); } + + if (bond_is_nondyn_tlb(bond)) + if (bond_tlb_update_slave_arr(bond, slave)) + pr_err("Failed to build slave-array for TLB mode.\n"); + } /* Caller must hold bond lock for read */ @@ -1718,6 +1758,11 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char */ } } + + if (bond_is_nondyn_tlb(bond)) { + if (bond_tlb_update_slave_arr(bond, NULL)) + pr_err("Failed to build slave-array for TLB mode.\n"); + } } /** |