diff options
author | Jay Vosburgh <fubar@us.ibm.com> | 2010-07-21 12:14:47 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-07-22 14:14:46 -0700 |
commit | f35188faa0fbabefac476536994f4b6f3677380f (patch) | |
tree | 460c54e3d8505fd0ce42d16a1798c1f8c371d4a9 /drivers/net/bonding/bond_main.c | |
parent | 4bee1f9ac066ef0350b961eab9fedc4d0bd0a549 (diff) | |
download | op-kernel-dev-f35188faa0fbabefac476536994f4b6f3677380f.zip op-kernel-dev-f35188faa0fbabefac476536994f4b6f3677380f.tar.gz |
bonding: change test for presence of VLANs
After commit ad1afb00393915a51c21b1ae8704562bf036855f
("vlan_dev: VLAN 0 should be treated as "no vlan tag" (802.1p packet)")
it is now regular practice for a VLAN "add vid" for VLAN 0 to
arrive prior to any VLAN registration or creation of a vlan_group.
This patch updates the bonding code that tests for the presence
of VLANs configured above bonding. The new logic tests for bond->vlgrp
to determine if a registration has occured, instead of testing that
bonding's internal vlan_list is empty.
The old code would panic when vlan_list was not empty, but
vlgrp was still NULL (because only an "add vid" for VLAN 0 had occured).
Bonding still adds VLAN 0 to its internal list so that 802.1p
frames are handled correctly on transmit when non-VLAN accelerated
slaves are members of the bond. The test against bond->vlan_list
remains in bond_dev_queue_xmit for this reason.
Modification to the bond->vlgrp now occurs under lock (in
addition to RTNL), because not all inspections of it occur under RTNL.
Additionally, because 8021q will never issue a "kill vid" for
VLAN 0, there is now logic in bond_uninit to release any remaining
entries from vlan_list.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Cc: Pedro Garcia <pedro.netdev@dondevamos.com>
Cc: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/bonding/bond_main.c')
-rw-r--r-- | drivers/net/bonding/bond_main.c | 30 |
1 files changed, 23 insertions, 7 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 20f45cb..f3b01ce 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -424,6 +424,7 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, { unsigned short uninitialized_var(vlan_id); + /* Test vlan_list not vlgrp to catch and handle 802.1p tags */ if (!list_empty(&bond->vlan_list) && !(slave_dev->features & NETIF_F_HW_VLAN_TX) && vlan_get_tag(skb, &vlan_id) == 0) { @@ -487,7 +488,9 @@ static void bond_vlan_rx_register(struct net_device *bond_dev, struct slave *slave; int i; + write_lock(&bond->lock); bond->vlgrp = grp; + write_unlock(&bond->lock); bond_for_each_slave(bond, slave, i) { struct net_device *slave_dev = slave->dev; @@ -569,7 +572,7 @@ static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *sla write_lock_bh(&bond->lock); - if (list_empty(&bond->vlan_list)) + if (!bond->vlgrp) goto out; if ((slave_dev->features & NETIF_F_HW_VLAN_RX) && @@ -596,7 +599,7 @@ static void bond_del_vlans_from_slave(struct bonding *bond, write_lock_bh(&bond->lock); - if (list_empty(&bond->vlan_list)) + if (!bond->vlgrp) goto out; if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) || @@ -604,6 +607,8 @@ static void bond_del_vlans_from_slave(struct bonding *bond, goto unreg; list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { + if (!vlan->vlan_id) + continue; /* Save and then restore vlan_dev in the grp array, * since the slave's driver might clear it. */ @@ -1443,7 +1448,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* no need to lock since we're protected by rtnl_lock */ if (slave_dev->features & NETIF_F_VLAN_CHALLENGED) { pr_debug("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name); - if (!list_empty(&bond->vlan_list)) { + if (bond->vlgrp) { pr_err("%s: Error: cannot enslave VLAN challenged slave %s on VLAN enabled bond %s\n", bond_dev->name, slave_dev->name, bond_dev->name); return -EPERM; @@ -1942,7 +1947,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) */ memset(bond_dev->dev_addr, 0, bond_dev->addr_len); - if (list_empty(&bond->vlan_list)) { + if (!bond->vlgrp) { bond_dev->features |= NETIF_F_VLAN_CHALLENGED; } else { pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n", @@ -2134,9 +2139,9 @@ static int bond_release_all(struct net_device *bond_dev) */ memset(bond_dev->dev_addr, 0, bond_dev->addr_len); - if (list_empty(&bond->vlan_list)) + if (!bond->vlgrp) { bond_dev->features |= NETIF_F_VLAN_CHALLENGED; - else { + } else { pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n", bond_dev->name, bond_dev->name); pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n", @@ -2569,7 +2574,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) if (!targets[i]) break; pr_debug("basa: target %x\n", targets[i]); - if (list_empty(&bond->vlan_list)) { + if (!bond->vlgrp) { pr_debug("basa: empty vlan: arp_send\n"); bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], bond->master_ip, 0); @@ -2658,6 +2663,9 @@ static void bond_send_gratuitous_arp(struct bonding *bond) bond->master_ip, 0); } + if (!bond->vlgrp) + return; + list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); if (vlan->vlan_ip) { @@ -3590,6 +3598,8 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, } list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { + if (!bond->vlgrp) + continue; vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); if (vlan_dev == event_dev) { switch (event) { @@ -4686,6 +4696,7 @@ static void bond_work_cancel_all(struct bonding *bond) static void bond_uninit(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct vlan_entry *vlan, *tmp; bond_netpoll_cleanup(bond_dev); @@ -4699,6 +4710,11 @@ static void bond_uninit(struct net_device *bond_dev) bond_remove_proc_entry(bond); __hw_addr_flush(&bond->mc_list); + + list_for_each_entry_safe(vlan, tmp, &bond->vlan_list, vlan_list) { + list_del(&vlan->vlan_list); + kfree(vlan); + } } /*------------------------- Module initialization ---------------------------*/ |