diff options
Diffstat (limited to 'drivers/net/bonding/bond_alb.c')
| -rw-r--r-- | drivers/net/bonding/bond_alb.c | 146 | 
1 files changed, 98 insertions, 48 deletions
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 76c0dade233f..95dd1f58c260 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -19,8 +19,6 @@   *   */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -  #include <linux/skbuff.h>  #include <linux/netdevice.h>  #include <linux/etherdevice.h> @@ -202,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); @@ -209,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) @@ -369,7 +372,7 @@ static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond,  	if (arp->op_code == htons(ARPOP_REPLY)) {  		/* update rx hash table for this ARP */  		rlb_update_entry_from_arp(bond, arp); -		pr_debug("Server received an ARP Reply from client\n"); +		netdev_dbg(bond->dev, "Server received an ARP Reply from client\n");  	}  out:  	return RX_HANDLER_ANOTHER; @@ -448,11 +451,13 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond)   */  static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[])  { -	if (!bond->curr_active_slave) +	struct slave *curr_active = bond_deref_active_protected(bond); + +	if (!curr_active)  		return;  	if (!bond->alb_info.primary_is_promisc) { -		if (!dev_set_promiscuity(bond->curr_active_slave->dev, 1)) +		if (!dev_set_promiscuity(curr_active->dev, 1))  			bond->alb_info.primary_is_promisc = 1;  		else  			bond->alb_info.primary_is_promisc = 0; @@ -460,7 +465,7 @@ static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[])  	bond->alb_info.rlb_promisc_timeout_counter = 0; -	alb_send_learning_packets(bond->curr_active_slave, addr, true); +	alb_send_learning_packets(curr_active, addr, true);  }  /* slave being removed should not be active at this point @@ -509,7 +514,7 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave)  	write_lock_bh(&bond->curr_slave_lock); -	if (slave != bond->curr_active_slave) +	if (slave != bond_deref_active_protected(bond))  		rlb_teach_disabled_mac_on_primary(bond, slave->dev->dev_addr);  	write_unlock_bh(&bond->curr_slave_lock); @@ -533,8 +538,8 @@ static void rlb_update_client(struct rlb_client_info *client_info)  				 client_info->slave->dev->dev_addr,  				 client_info->mac_dst);  		if (!skb) { -			pr_err("%s: Error: failed to create an ARP packet\n", -			       client_info->slave->bond->dev->name); +			netdev_err(client_info->slave->bond->dev, +				   "failed to create an ARP packet\n");  			continue;  		} @@ -543,8 +548,8 @@ static void rlb_update_client(struct rlb_client_info *client_info)  		if (client_info->vlan_id) {  			skb = vlan_put_tag(skb, htons(ETH_P_8021Q), client_info->vlan_id);  			if (!skb) { -				pr_err("%s: Error: failed to insert VLAN tag\n", -				       client_info->slave->bond->dev->name); +				netdev_err(client_info->slave->bond->dev, +					   "failed to insert VLAN tag\n");  				continue;  			}  		} @@ -628,8 +633,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip)  		client_info = &(bond_info->rx_hashtbl[hash_index]);  		if (!client_info->slave) { -			pr_err("%s: Error: found a client with no channel in the client's hash table\n", -			       bond->dev->name); +			netdev_err(bond->dev, "found a client with no channel in the client's hash table\n");  			continue;  		}  		/*update all clients using this src_ip, that are not assigned @@ -684,7 +688,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon  			 * move the old client to primary (curr_active_slave) so  			 * that the new client can be assigned to this entry.  			 */ -			if (bond->curr_active_slave && +			if (curr_active_slave &&  			    client_info->slave != curr_active_slave) {  				client_info->slave = curr_active_slave;  				rlb_update_client(client_info); @@ -765,7 +769,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)  		tx_slave = rlb_choose_channel(skb, bond);  		if (tx_slave)  			ether_addr_copy(arp->mac_src, tx_slave->dev->dev_addr); -		pr_debug("Server sent ARP Reply packet\n"); +		netdev_dbg(bond->dev, "Server sent ARP Reply packet\n");  	} else if (arp->op_code == htons(ARPOP_REQUEST)) {  		/* Create an entry in the rx_hashtbl for this client as a  		 * place holder. @@ -785,7 +789,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)  		 * updated with their assigned mac.  		 */  		rlb_req_update_subnet_clients(bond, arp->ip_src); -		pr_debug("Server sent ARP Request packet\n"); +		netdev_dbg(bond->dev, "Server sent ARP Request packet\n");  	}  	return tx_slave; @@ -1024,8 +1028,7 @@ static void alb_send_lp_vid(struct slave *slave, u8 mac_addr[],  	if (vid) {  		skb = vlan_put_tag(skb, vlan_proto, vid);  		if (!skb) { -			pr_err("%s: Error: failed to insert VLAN tag\n", -			       slave->bond->dev->name); +			netdev_err(slave->bond->dev, "failed to insert VLAN tag\n");  			return;  		}  	} @@ -1039,7 +1042,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],  	struct bonding *bond = bond_get_bond_by_slave(slave);  	struct net_device *upper;  	struct list_head *iter; -	struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP]; +	struct bond_vlan_tag *tags;  	/* send untagged */  	alb_send_lp_vid(slave, mac_addr, 0, 0); @@ -1067,10 +1070,12 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],  		 * when strict_match is turned off.  		 */  		if (netif_is_macvlan(upper) && !strict_match) { -			memset(tags, 0, sizeof(tags)); -			bond_verify_device_path(bond->dev, upper, tags); +			tags = bond_verify_device_path(bond->dev, upper, 0); +			if (IS_ERR_OR_NULL(tags)) +				BUG();  			alb_send_lp_vid(slave, upper->dev_addr,  					tags[0].vlan_proto, tags[0].vlan_id); +			kfree(tags);  		}  	}  	rcu_read_unlock(); @@ -1091,9 +1096,8 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])  	memcpy(s_addr.sa_data, addr, dev->addr_len);  	s_addr.sa_family = dev->type;  	if (dev_set_mac_address(dev, &s_addr)) { -		pr_err("%s: Error: dev_set_mac_address of dev %s failed!\n" -		       "ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n", -		       slave->bond->dev->name, dev->name); +		netdev_err(slave->bond->dev, "dev_set_mac_address of dev %s failed! ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n", +			   dev->name);  		return -EOPNOTSUPP;  	}  	return 0; @@ -1221,7 +1225,7 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla   */  static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave)  { -	struct slave *has_bond_addr = bond->curr_active_slave; +	struct slave *has_bond_addr = rcu_access_pointer(bond->curr_active_slave);  	struct slave *tmp_slave1, *free_mac_slave = NULL;  	struct list_head *iter; @@ -1267,13 +1271,12 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav  	if (free_mac_slave) {  		alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr); -		pr_warn("%s: Warning: the hw address of slave %s is in use by the bond; giving it the hw address of %s\n", -			bond->dev->name, slave->dev->name, -			free_mac_slave->dev->name); +		netdev_warn(bond->dev, "the hw address of slave %s is in use by the bond; giving it the hw address of %s\n", +			    slave->dev->name, free_mac_slave->dev->name);  	} else if (has_bond_addr) { -		pr_err("%s: Error: the hw address of slave %s is in use by the bond; couldn't find a slave with a free hw address to give it (this should not have happened)\n", -		       bond->dev->name, slave->dev->name); +		netdev_err(bond->dev, "the hw address of slave %s is in use by the bond; couldn't find a slave with a free hw address to give it (this should not have happened)\n", +			   slave->dev->name);  		return -EFAULT;  	} @@ -1406,9 +1409,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; @@ -1429,12 +1462,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;  		} @@ -1575,7 +1608,7 @@ void bond_alb_monitor(struct work_struct *work)  			 * use mac of the slave device.  			 * In RLB mode, we always use strict matches.  			 */ -			strict_match = (slave != bond->curr_active_slave || +			strict_match = (slave != rcu_access_pointer(bond->curr_active_slave) ||  					bond_info->rlb_enabled);  			alb_send_learning_packets(slave, slave->dev->dev_addr,  						  strict_match); @@ -1593,7 +1626,7 @@ void bond_alb_monitor(struct work_struct *work)  		bond_for_each_slave_rcu(bond, slave, iter) {  			tlb_clear_slave(bond, slave, 1); -			if (slave == bond->curr_active_slave) { +			if (slave == rcu_access_pointer(bond->curr_active_slave)) {  				SLAVE_TLB_INFO(slave).load =  					bond_info->unbalanced_load /  						BOND_TLB_REBALANCE_INTERVAL; @@ -1625,7 +1658,8 @@ void bond_alb_monitor(struct work_struct *work)  			 * because a slave was disabled then  			 * it can now leave promiscuous mode.  			 */ -			dev_set_promiscuity(bond->curr_active_slave->dev, -1); +			dev_set_promiscuity(rtnl_dereference(bond->curr_active_slave)->dev, +					    -1);  			bond_info->primary_is_promisc = 0;  			rtnl_unlock(); @@ -1698,6 +1732,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 */ @@ -1721,6 +1760,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"); +	}  }  /** @@ -1742,17 +1786,21 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave  	__acquires(&bond->curr_slave_lock)  {  	struct slave *swap_slave; +	struct slave *curr_active; -	if (bond->curr_active_slave == new_slave) +	curr_active = rcu_dereference_protected(bond->curr_active_slave, +						!new_slave || +						lockdep_is_held(&bond->curr_slave_lock)); +	if (curr_active == new_slave)  		return; -	if (bond->curr_active_slave && bond->alb_info.primary_is_promisc) { -		dev_set_promiscuity(bond->curr_active_slave->dev, -1); +	if (curr_active && bond->alb_info.primary_is_promisc) { +		dev_set_promiscuity(curr_active->dev, -1);  		bond->alb_info.primary_is_promisc = 0;  		bond->alb_info.rlb_promisc_timeout_counter = 0;  	} -	swap_slave = bond->curr_active_slave; +	swap_slave = curr_active;  	rcu_assign_pointer(bond->curr_active_slave, new_slave);  	if (!new_slave || !bond_has_slaves(bond)) @@ -1818,6 +1866,7 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)  {  	struct bonding *bond = netdev_priv(bond_dev);  	struct sockaddr *sa = addr; +	struct slave *curr_active;  	struct slave *swap_slave;  	int res; @@ -1834,23 +1883,24 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)  	 * Otherwise we'll need to pass the new address to it and handle  	 * duplications.  	 */ -	if (!bond->curr_active_slave) +	curr_active = rtnl_dereference(bond->curr_active_slave); +	if (!curr_active)  		return 0;  	swap_slave = bond_slave_has_mac(bond, bond_dev->dev_addr);  	if (swap_slave) { -		alb_swap_mac_addr(swap_slave, bond->curr_active_slave); -		alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave); +		alb_swap_mac_addr(swap_slave, curr_active); +		alb_fasten_mac_swap(bond, swap_slave, curr_active);  	} else { -		alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr); +		alb_set_slave_mac_addr(curr_active, bond_dev->dev_addr);  		read_lock(&bond->lock); -		alb_send_learning_packets(bond->curr_active_slave, +		alb_send_learning_packets(curr_active,  					  bond_dev->dev_addr, false);  		if (bond->alb_info.rlb_enabled) {  			/* inform clients mac address has changed */ -			rlb_req_update_slave_clients(bond, bond->curr_active_slave); +			rlb_req_update_slave_clients(bond, curr_active);  		}  		read_unlock(&bond->lock);  	}  | 
