aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Vosburgh <fubar@us.ibm.com>2007-02-28 20:03:37 -0500
committerJeff Garzik <jeff@garzik.org>2007-03-06 06:08:11 -0500
commita816c7c712ff9f6770168b91facb9bfa9f0acd48 (patch)
treeaa47edb0c1e23be76d0fff783b9665221f2a84ef
parente245cb71d490e5e516c0ca0688fad7de6c22943d (diff)
bonding: Improve IGMP join processing
In active-backup mode, the current bonding code duplicates IGMP traffic to all slaves, so that switches are up to date in case of a failover from an active to a backup interface. If bonding then fails back to the original active interface, it is likely that the "active slave" switch's IGMP forwarding for the port will be out of date until some event occurs to refresh the switch (e.g., a membership query). This patch alters the behavior of bonding to no longer flood IGMP to all ports, and to issue IGMP JOINs to the newly active port at the time of a failover. This insures that switches are kept up to date for all cases. "GOELLESCH Niels" <niels.goellesch@eurocontrol.int> originally reported this problem, and included a patch. His original patch was modified by Jay Vosburgh to additionally remove the existing IGMP flood behavior, use RCU, streamline code paths, fix trailing white space, and adjust for style. Signed-off-by: Jay Vosburgh <fubar@us.ibm.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r--drivers/net/bonding/bond_main.c76
-rw-r--r--include/linux/igmp.h2
-rw-r--r--net/ipv4/igmp.c23
3 files changed, 50 insertions, 51 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 1ca73b8c139b..e4724d874e7c 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -60,6 +60,7 @@
60#include <linux/errno.h> 60#include <linux/errno.h>
61#include <linux/netdevice.h> 61#include <linux/netdevice.h>
62#include <linux/inetdevice.h> 62#include <linux/inetdevice.h>
63#include <linux/igmp.h>
63#include <linux/etherdevice.h> 64#include <linux/etherdevice.h>
64#include <linux/skbuff.h> 65#include <linux/skbuff.h>
65#include <net/sock.h> 66#include <net/sock.h>
@@ -861,6 +862,28 @@ static void bond_mc_delete(struct bonding *bond, void *addr, int alen)
861 } 862 }
862} 863}
863 864
865
866/*
867 * Retrieve the list of registered multicast addresses for the bonding
868 * device and retransmit an IGMP JOIN request to the current active
869 * slave.
870 */
871static void bond_resend_igmp_join_requests(struct bonding *bond)
872{
873 struct in_device *in_dev;
874 struct ip_mc_list *im;
875
876 rcu_read_lock();
877 in_dev = __in_dev_get_rcu(bond->dev);
878 if (in_dev) {
879 for (im = in_dev->mc_list; im; im = im->next) {
880 ip_mc_rejoin_group(im);
881 }
882 }
883
884 rcu_read_unlock();
885}
886
864/* 887/*
865 * Totally destroys the mc_list in bond 888 * Totally destroys the mc_list in bond
866 */ 889 */
@@ -874,6 +897,7 @@ static void bond_mc_list_destroy(struct bonding *bond)
874 kfree(dmi); 897 kfree(dmi);
875 dmi = bond->mc_list; 898 dmi = bond->mc_list;
876 } 899 }
900 bond->mc_list = NULL;
877} 901}
878 902
879/* 903/*
@@ -967,6 +991,7 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, struct
967 for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next) { 991 for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next) {
968 dev_mc_add(new_active->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); 992 dev_mc_add(new_active->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
969 } 993 }
994 bond_resend_igmp_join_requests(bond);
970 } 995 }
971} 996}
972 997
@@ -4017,42 +4042,6 @@ out:
4017 return 0; 4042 return 0;
4018} 4043}
4019 4044
4020static void bond_activebackup_xmit_copy(struct sk_buff *skb,
4021 struct bonding *bond,
4022 struct slave *slave)
4023{
4024 struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
4025 struct ethhdr *eth_data;
4026 u8 *hwaddr;
4027 int res;
4028
4029 if (!skb2) {
4030 printk(KERN_ERR DRV_NAME ": Error: "
4031 "bond_activebackup_xmit_copy(): skb_copy() failed\n");
4032 return;
4033 }
4034
4035 skb2->mac.raw = (unsigned char *)skb2->data;
4036 eth_data = eth_hdr(skb2);
4037
4038 /* Pick an appropriate source MAC address
4039 * -- use slave's perm MAC addr, unless used by bond
4040 * -- otherwise, borrow active slave's perm MAC addr
4041 * since that will not be used
4042 */
4043 hwaddr = slave->perm_hwaddr;
4044 if (!memcmp(eth_data->h_source, hwaddr, ETH_ALEN))
4045 hwaddr = bond->curr_active_slave->perm_hwaddr;
4046
4047 /* Set source MAC address appropriately */
4048 memcpy(eth_data->h_source, hwaddr, ETH_ALEN);
4049
4050 res = bond_dev_queue_xmit(bond, skb2, slave->dev);
4051 if (res)
4052 dev_kfree_skb(skb2);
4053
4054 return;
4055}
4056 4045
4057/* 4046/*
4058 * in active-backup mode, we know that bond->curr_active_slave is always valid if 4047 * in active-backup mode, we know that bond->curr_active_slave is always valid if
@@ -4073,21 +4062,6 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d
4073 if (!bond->curr_active_slave) 4062 if (!bond->curr_active_slave)
4074 goto out; 4063 goto out;
4075 4064
4076 /* Xmit IGMP frames on all slaves to ensure rapid fail-over
4077 for multicast traffic on snooping switches */
4078 if (skb->protocol == __constant_htons(ETH_P_IP) &&
4079 skb->nh.iph->protocol == IPPROTO_IGMP) {
4080 struct slave *slave, *active_slave;
4081 int i;
4082
4083 active_slave = bond->curr_active_slave;
4084 bond_for_each_slave_from_to(bond, slave, i, active_slave->next,
4085 active_slave->prev)
4086 if (IS_UP(slave->dev) &&
4087 (slave->link == BOND_LINK_UP))
4088 bond_activebackup_xmit_copy(skb, bond, slave);
4089 }
4090
4091 res = bond_dev_queue_xmit(bond, skb, bond->curr_active_slave->dev); 4065 res = bond_dev_queue_xmit(bond, skb, bond->curr_active_slave->dev);
4092 4066
4093out: 4067out:
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 9dbb525c5178..a113fe68d8a1 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -218,5 +218,7 @@ extern void ip_mc_up(struct in_device *);
218extern void ip_mc_down(struct in_device *); 218extern void ip_mc_down(struct in_device *);
219extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr); 219extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr);
220extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr); 220extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr);
221extern void ip_mc_rejoin_group(struct ip_mc_list *im);
222
221#endif 223#endif
222#endif 224#endif
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 063721302ebf..1c6a084b5fb7 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1251,6 +1251,28 @@ out:
1251} 1251}
1252 1252
1253/* 1253/*
1254 * Resend IGMP JOIN report; used for bonding.
1255 */
1256void ip_mc_rejoin_group(struct ip_mc_list *im)
1257{
1258 struct in_device *in_dev = im->interface;
1259
1260#ifdef CONFIG_IP_MULTICAST
1261 if (im->multiaddr == IGMP_ALL_HOSTS)
1262 return;
1263
1264 if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) {
1265 igmp_mod_timer(im, IGMP_Initial_Report_Delay);
1266 return;
1267 }
1268 /* else, v3 */
1269 im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
1270 IGMP_Unsolicited_Report_Count;
1271 igmp_ifc_event(in_dev);
1272#endif
1273}
1274
1275/*
1254 * A socket has left a multicast group on device dev 1276 * A socket has left a multicast group on device dev
1255 */ 1277 */
1256 1278
@@ -2596,3 +2618,4 @@ int __init igmp_mc_proc_init(void)
2596EXPORT_SYMBOL(ip_mc_dec_group); 2618EXPORT_SYMBOL(ip_mc_dec_group);
2597EXPORT_SYMBOL(ip_mc_inc_group); 2619EXPORT_SYMBOL(ip_mc_inc_group);
2598EXPORT_SYMBOL(ip_mc_join_group); 2620EXPORT_SYMBOL(ip_mc_join_group);
2621EXPORT_SYMBOL(ip_mc_rejoin_group);