patch-2.4.23 linux-2.4.23/drivers/net/bonding/bond_main.c

Next file: linux-2.4.23/drivers/net/bonding/bonding.h
Previous file: linux-2.4.23/drivers/net/bonding/bond_alb.h
Back to the patch index
Back to the overall index

diff -urN linux-2.4.22/drivers/net/bonding/bond_main.c linux-2.4.23/drivers/net/bonding/bond_main.c
@@ -278,7 +278,7 @@
  *	  bonding round-robin mode ignoring links after failover/recovery
  *
  * 2003/03/17 - Jay Vosburgh <fubar at us dot ibm dot com>
- *	- kmalloc fix (GPF_KERNEL to GPF_ATOMIC) reported by
+ *	- kmalloc fix (GFP_KERNEL to GFP_ATOMIC) reported by
  *	  Shmulik dot Hen at intel.com.
  *	- Based on discussion on mailing list, changed use of
  *	  update_slave_cnt(), created wrapper functions for adding/removing
@@ -323,22 +323,22 @@
  * 2003/03/18 - Amir Noam <amir.noam at intel dot com>,
  *		Tsippy Mendelson <tsippy.mendelson at intel dot com> and
  *		Shmulik Hen <shmulik.hen at intel dot com>
- *    - Added support for IEEE 802.3ad Dynamic link aggregation mode.
+ *	- Added support for IEEE 802.3ad Dynamic link aggregation mode.
  *
  * 2003/05/01 - Amir Noam <amir.noam at intel dot com>
- *    - Added ABI version control to restore compatibility between
- *      new/old ifenslave and new/old bonding.
+ *	- Added ABI version control to restore compatibility between
+ *	  new/old ifenslave and new/old bonding.
  *
  * 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
- *    - Fixed bug in bond_release_all(): save old value of current_slave
- *      before setting it to NULL.
- *    - Changed driver versioning scheme to include version number instead
- *      of release date (that is already in another field). There are 3
- *      fields X.Y.Z where:
- *            X - Major version - big behavior changes
- *            Y - Minor version - addition of features
- *            Z - Extra version - minor changes and bug fixes
- *      The current version is 1.0.0 as a base line.
+ *	- Fixed bug in bond_release_all(): save old value of current_slave
+ *	  before setting it to NULL.
+ *	- Changed driver versioning scheme to include version number instead
+ *	  of release date (that is already in another field). There are 3
+ *	  fields X.Y.Z where:
+ *		X - Major version - big behavior changes
+ *		Y - Minor version - addition of features
+ *		Z - Extra version - minor changes and bug fixes
+ *	  The current version is 1.0.0 as a base line.
  *
  * 2003/05/01 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
  *		Amir Noam <amir.noam at intel dot com>
@@ -382,12 +382,50 @@
  *	  ABI version if receiving unsupported ioctl commands.
  *
  * 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com>
+ *	- Fix ifenslave -c causing bond to loose existing routes;
+ *	  added bond_set_mac_address() that doesn't require the
+ *	  bond to be down.
  *	- In conjunction with fix for ifenslave -c, in
  *	  bond_change_active(), changing to the already active slave
  *	  is no longer an error (it successfully does nothing).
  *
  * 2003/06/30 - Amir Noam <amir.noam at intel dot com>
  * 	- Fixed bond_change_active() for ALB/TLB modes.
+ *	  Version to 2.2.14.
+ *
+ * 2003/07/29 - Amir Noam <amir.noam at intel dot com>
+ * 	- Fixed ARP monitoring bug.
+ *	  Version to 2.2.15.
+ *
+ * 2003/07/31 - Willy Tarreau <willy at ods dot org>
+ * 	- Fixed kernel panic when using ARP monitoring without
+ *	  setting bond's IP address.
+ *	  Version to 2.2.16.
+ *
+ * 2003/08/06 - Amir Noam <amir.noam at intel dot com>
+ * 	- Back port from 2.6: use alloc_netdev(); fix /proc handling;
+ *	  made stats a part of bond struct so no need to allocate
+ *	  and free it separately; use standard list operations instead
+ *	  of pre-allocated array of bonds.
+ *	  Version to 2.3.0.
+ *
+ * 2003/08/07 - Jay Vosburgh <fubar at us dot ibm dot com>,
+ *	       Amir Noam <amir.noam at intel dot com> and
+ *	       Shmulik Hen <shmulik.hen at intel dot com>
+ *	- Propagating master's settings: Distinguish between modes that
+ *	  use a primary slave from those that don't, and propagate settings
+ *	  accordingly; Consolidate change_active opeartions and add
+ *	  reselect_active and find_best opeartions; Decouple promiscuous
+ *	  handling from the multicast mode setting; Add support for changing
+ *	  HW address and MTU with proper unwind; Consolidate procfs code,
+ *	  add CHANGENAME handler; Enhance netdev notification handling.
+ *	  Version to 2.4.0.
+ *
+ * 2003/09/15 - Stephen Hemminger <shemminger at osdl dot org>,
+ *	       Amir Noam <amir.noam at intel dot com>
+ *	- Convert /proc to seq_file interface.
+ *	  Change /proc/net/bondX/info to /proc/net/bonding/bondX.
+ *	  Set version to 2.4.1.
  */
 
 #include <linux/config.h>
@@ -421,6 +459,8 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <linux/rtnetlink.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 
 #include <linux/if_bonding.h>
 #include <linux/smp.h>
@@ -432,10 +472,10 @@
 #include "bond_3ad.h"
 #include "bond_alb.h"
 
-#define DRV_VERSION		"2.2.14"
-#define DRV_RELDATE		"June 30, 2003"
-#define DRV_NAME		"bonding"
-#define DRV_DESCRIPTION		"Ethernet Channel Bonding Driver"
+#define DRV_VERSION	"2.4.1"
+#define DRV_RELDATE	"September 15, 2003"
+#define DRV_NAME	"bonding"
+#define DRV_DESCRIPTION	"Ethernet Channel Bonding Driver"
 
 static const char *version =
 DRV_NAME ".c:v" DRV_VERSION " (" DRV_RELDATE ")\n";
@@ -453,6 +493,11 @@
 #define MAX_ARP_IP_TARGETS 16
 #endif
 
+#define USES_PRIMARY(mode) \
+		(((mode) == BOND_MODE_ACTIVEBACKUP) || \
+		 ((mode) == BOND_MODE_TLB) || \
+		 ((mode) == BOND_MODE_ALB))
+
 struct bond_parm_tbl {
 	char *modename;
 	int mode;
@@ -515,9 +560,10 @@
 {	NULL,		-1},
 };
 
-static int first_pass	= 1;
-static struct bonding *these_bonds =  NULL;
-static struct net_device *dev_bonds = NULL;
+static LIST_HEAD(bond_dev_list);
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *bond_proc_dir = NULL;
+#endif
 
 MODULE_PARM(max_bonds, "i");
 MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
@@ -549,7 +595,6 @@
 static void bond_mii_monitor(struct net_device *dev);
 static void loadbalance_arp_monitor(struct net_device *dev);
 static void activebackup_arp_monitor(struct net_device *dev);
-static int bond_event(struct notifier_block *this, unsigned long event, void *ptr);
 static void bond_mc_list_destroy(struct bonding *bond);
 static void bond_mc_add(bonding_t *bond, void *addr, int alen);
 static void bond_mc_delete(bonding_t *bond, void *addr, int alen);
@@ -563,30 +608,17 @@
 static int bond_release(struct net_device *master, struct net_device *slave);
 static int bond_release_all(struct net_device *master);
 static int bond_sethwaddr(struct net_device *master, struct net_device *slave);
+static void change_active_interface(struct bonding *bond, struct slave *new);
+static void reselect_active_interface(struct bonding *bond);
+static struct slave *find_best_interface(struct bonding *bond);
 
-/*
- * bond_get_info is the interface into the /proc filesystem.  This is
- * a different interface than the BOND_INFO_QUERY ioctl.  That is done
- * through the generic networking ioctl interface, and bond_info_query
- * is the internal function which provides that information.
- */
-static int bond_get_info(char *buf, char **start, off_t offset, int length);
-
-/* Caller must hold bond->ptrlock for write */
-static inline struct slave*
-bond_assign_current_slave(struct bonding *bond,struct slave *newslave)
-{
-	if ((bond_mode == BOND_MODE_TLB) ||
-	    (bond_mode == BOND_MODE_ALB)) {
-		bond_alb_assign_current_slave(bond, newslave);
-	} else {
-		bond->current_slave = newslave;
-	}
-
-	return bond->current_slave;
-}
 
 /* #define BONDING_DEBUG 1 */
+#ifdef BONDING_DEBUG
+#define dprintk(x...) printk(x...)
+#else /* BONDING_DEBUG */
+#define dprintk(x...) do {} while (0)
+#endif /* BONDING_DEBUG */
 
 /* several macros */
 
@@ -675,26 +707,13 @@
 		BUG();
 }
 
-/*
- * Set MAC.  Differs from eth_mac_addr in that we allow changes while
- * netif_running().
- */
-static int
-bond_set_mac_address(struct net_device *dev, void *p)
-{
-	struct sockaddr *addr = p;
-
-	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-	return 0;
-}
-
 /* 
  * This function detaches the slave <slave> from the list <bond>.
  * WARNING: no check is made to verify if the slave effectively
  * belongs to <bond>. It returns <slave> in case it's needed.
  * Nothing is freed on return, structures are just unchained.
  * If the bond->current_slave pointer was pointing to <slave>,
- * it's replaced with bond->next, or NULL if not applicable.
+ * it should be changed by the calling function.
  *
  * bond->lock held for writing by caller.
  */
@@ -728,17 +747,6 @@
 
 	update_slave_cnt(bond, -1);
 
-	/* no need to hold ptrlock since bond lock is
-	 * already held for writing
-	 */
-	if (slave == bond->current_slave) {
-		if ( bond->next != (slave_t *)bond) {  /* found one slave */
-			bond_assign_current_slave(bond, bond->next);
-		} else {
-			bond_assign_current_slave(bond, NULL);
-		}
-	}
-
 	return slave;
 }
 
@@ -983,8 +991,6 @@
 		add_timer(alb_timer);
 	}
 
-	MOD_INC_USE_COUNT;
-
 	if (miimon > 0) {  /* link check interval, in milliseconds. */
 		init_timer(timer);
 		timer->expires  = jiffies + (miimon * HZ / 1000);
@@ -1058,7 +1064,6 @@
 		bond_alb_deinitialize(bond);
 	}
 
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1173,23 +1178,22 @@
 } 
 
 /*
- * Push the promiscuity flag down to all slaves
+ * Push the promiscuity flag down to appropriate slaves
  */
 static void bond_set_promiscuity(bonding_t *bond, int inc)
 { 
 	slave_t *slave; 
-	switch (multicast_mode) {
-	case BOND_MULTICAST_ACTIVE :
-		/* write lock already acquired */
-		if (bond->current_slave != NULL)
+
+	if (USES_PRIMARY(bond_mode)) {
+		if (bond->current_slave) {
 			dev_set_promiscuity(bond->current_slave->dev, inc);
-		break;
-	case BOND_MULTICAST_ALL :
-		for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
+		}
+
+	} else { 
+		for (slave = bond->prev; slave != (slave_t*)bond;
+		     slave = slave->prev) {
 			dev_set_promiscuity(slave->dev, inc);
-		break;
-	case BOND_MULTICAST_DISABLED :
-		break;
+		}
 	}
 } 
 
@@ -1235,20 +1239,23 @@
 	bonding_t *bond = master->priv;
 	struct dev_mc_list *dmi;
 
-	if (multicast_mode == BOND_MULTICAST_DISABLED)
-		return;
-	/*
-	 * Lock the private data for the master
-	 */
 	write_lock_bh(&bond->lock);
 
-	/* set promiscuity flag to slaves */
+	/*
+	 * Do promisc before checking multicast_mode
+	 */
 	if ( (master->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC) )
 		bond_set_promiscuity(bond, 1); 
 
 	if ( !(master->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC) ) 
 		bond_set_promiscuity(bond, -1); 
 
+	if (multicast_mode == BOND_MULTICAST_DISABLED) {
+		bond->flags = master->flags;
+		write_unlock_bh(&bond->lock);
+		return;
+	}
+
 	/* set allmulti flag to slaves */ 
 	if ( (master->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI) ) 
 		bond_set_allmulti(bond, 1); 
@@ -1280,32 +1287,40 @@
 
 /*
  * Update the mc list and multicast-related flags for the new and 
- * old active slaves (if any) according to the multicast mode
+ * old active slaves (if any) according to the multicast mode, and
+ * promiscuous flags unconditionally.
  */
 static void bond_mc_update(bonding_t *bond, slave_t *new, slave_t *old)
 {
 	struct dev_mc_list *dmi;
 
-	switch(multicast_mode) {
-	case BOND_MULTICAST_ACTIVE :		
+	if (USES_PRIMARY(bond_mode)) {
 		if (bond->device->flags & IFF_PROMISC) {
-			if (old != NULL && new != old)
+			if (old)
 				dev_set_promiscuity(old->dev, -1);
-			dev_set_promiscuity(new->dev, 1);
+			if (new)
+				dev_set_promiscuity(new->dev, 1);
 		}
+	}
+
+	switch(multicast_mode) {
+	case BOND_MULTICAST_ACTIVE :		
 		if (bond->device->flags & IFF_ALLMULTI) {
-			if (old != NULL && new != old)
+			if (old)
 				dev_set_allmulti(old->dev, -1);
-			dev_set_allmulti(new->dev, 1);
+			if (new)
+				dev_set_allmulti(new->dev, 1);
 		}
 		/* first remove all mc addresses from old slave if any,
 		   and _then_ add them to new active slave */
-		if (old != NULL && new != old) {
+		if (old) {
 			for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next)
 				dev_mc_delete(old->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
 		}
-		for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next)
-			dev_mc_add(new->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
+		if (new) {
+			for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next)
+				dev_mc_add(new->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
+		}
 		break;
 	case BOND_MULTICAST_ALL :
 		/* nothing to do: mc list is already up-to-date on all slaves */
@@ -1421,7 +1436,7 @@
 			 * The application already set the master's
 			 * mac address to that of the first slave
 			 */
-			memcpy(addr.sa_data, master_dev->dev_addr, ETH_ALEN);
+			memcpy(addr.sa_data, master_dev->dev_addr, master_dev->addr_len);
 			addr.sa_family = slave_dev->type;
 			err = slave_dev->set_mac_address(slave_dev, &addr);
 			if (err) {
@@ -1467,11 +1482,19 @@
 		}
 	}
 
-	if (multicast_mode == BOND_MULTICAST_ALL) {
-		/* set promiscuity level to new slave */ 
-		if (master_dev->flags & IFF_PROMISC)
+	/* set promiscuity level to new slave */ 
+	if (master_dev->flags & IFF_PROMISC) {
+		/* If the mode USES_PRIMARY, then the new slave gets the
+		 * master's promisc (and mc) settings only if it becomes the
+		 * current_slave, and that is taken care of later when calling
+		 * bond_change_active()
+		 */
+		if (!USES_PRIMARY(bond_mode)) {
 			dev_set_promiscuity(slave_dev, 1); 
+		}
+	}
  
+	if (multicast_mode == BOND_MULTICAST_ALL) {
 		/* set allmulti level to new slave */
 		if (master_dev->flags & IFF_ALLMULTI) 
 			dev_set_allmulti(slave_dev, 1); 
@@ -1584,9 +1607,7 @@
 #endif
 			/* first slave or no active slave yet, and this link
 			   is OK, so make this interface the active one */
-			bond_assign_current_slave(bond, new_slave);
-			bond_set_slave_active_flags(new_slave);
-			bond_mc_update(bond, new_slave, NULL);
+			change_active_interface(bond, new_slave);
 		}
 		else {
 #ifdef BONDING_DEBUG
@@ -1636,7 +1657,7 @@
 			/* first slave or no active slave yet, and this link
 			 * is OK, so make this interface the active one
 			 */
-			bond_assign_current_slave(bond, new_slave);
+			change_active_interface(bond, new_slave);
 		}
 
 		/* if there is a primary slave, remember it */
@@ -1651,8 +1672,13 @@
 #endif
 		/* always active in trunk mode */
 		new_slave->state = BOND_STATE_ACTIVE;
+
+		/* In trunking mode there is little meaning to current_slave
+		 * anyway (it holds no special properties of the bond device),
+		 * so we can change it without calling change_active_interface()
+		 */
 		if (bond->current_slave == NULL) 
-			bond_assign_current_slave(bond, new_slave);
+			bond->current_slave = new_slave;
 	}
 
 	write_unlock_bh(&bond->lock);
@@ -1743,6 +1769,13 @@
 		return -ENODEV;
 	}
 
+	/* Verify that master_dev is indeed the master of slave_dev */
+	if (!(slave_dev->flags & IFF_SLAVE) ||
+	    (slave_dev->master != master_dev)) {
+
+		return -EINVAL;
+	}
+
 	bond = (struct bonding *) master_dev->priv;
 	write_lock_bh(&bond->lock);
 	slave = (slave_t *)bond;
@@ -1767,16 +1800,7 @@
 	    (oldactive != NULL)&&
 	    (newactive->link == BOND_LINK_UP)&&
 	    IS_UP(newactive->dev)) {
-		if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
-			bond_set_slave_inactive_flags(oldactive);
-			bond_set_slave_active_flags(newactive);
-		}
-
-		bond_mc_update(bond, newactive, oldactive);
-		bond_assign_current_slave(bond, newactive);
-		printk("%s : activate %s(old : %s)\n",
-			master_dev->name, newactive->dev->name, 
-			oldactive->dev->name);
+		change_active_interface(bond, newactive);
 	} else {
 		ret = -EINVAL;
 	}
@@ -1784,47 +1808,26 @@
 	return ret;
 }
 
-/* Choose a new valid interface from the pool, set it active
- * and make it the current slave. If no valid interface is
- * found, the oldest slave in BACK state is choosen and
- * activated. If none is found, it's considered as no
- * interfaces left so the current slave is set to NULL.
- * The result is a pointer to the current slave.
- *
- * Since this function sends messages tails through printk, the caller
- * must have started something like `printk(KERN_INFO "xxxx ");'.
+/**
+ * find_best_interface - select the best available slave to be the active one
+ * @bond: our bonding struct
  *
  * Warning: Caller must hold ptrlock for writing.
  */
-slave_t *change_active_interface(bonding_t *bond)
+static struct slave *find_best_interface(struct bonding *bond)
 {
-	slave_t *newslave, *oldslave;
-	slave_t *bestslave = NULL;
+	struct slave *newslave, *oldslave;
+	struct slave *bestslave = NULL;
 	int mintime;
 
 	newslave = oldslave = bond->current_slave;
 
 	if (newslave == NULL) { /* there were no active slaves left */
 		if (bond->next != (slave_t *)bond) {  /* found one slave */
-			newslave = bond_assign_current_slave(bond, bond->next);
+			newslave = bond->next;
 		} else {
-
-			printk (" but could not find any %s interface.\n",
-				(bond_mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other");
-			bond_assign_current_slave(bond, NULL);
 			return NULL; /* still no slave, return NULL */
 		}
-	} else if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
-		/* make sure oldslave doesn't send arps - this could
-		 * cause a ping-pong effect between interfaces since they
-		 * would be able to tx arps - in active backup only one
-		 * slave should be able to tx arps, and that should be
-		 * the current_slave; the only exception is when all
-		 * slaves have gone down, then only one non-current slave can
-		 * send arps at a time; clearing oldslaves' mc list is handled
-		 * later in this function.
-		 */
-		bond_set_slave_inactive_flags(oldslave);
 	}
 
 	mintime = updelay;
@@ -1839,22 +1842,12 @@
 			newslave = bond->primary_slave;
 	}
 
+	/* remember where to stop iterating over the slaves */
+	oldslave = newslave;
+
 	do {
 		if (IS_UP(newslave->dev)) {
 			if (newslave->link == BOND_LINK_UP) {
-				/* this one is immediately usable */
-				if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
-					bond_set_slave_active_flags(newslave);
-					bond_mc_update(bond, newslave, oldslave);
-					printk (" and making interface %s the active one.\n",
-						newslave->dev->name);
-				}
-				else {
-					printk (" and setting pointer to interface %s.\n",
-						newslave->dev->name);
-				}
-
-				bond_assign_current_slave(bond, newslave);
 				return newslave;
 			}
 			else if (newslave->link == BOND_LINK_BACK) {
@@ -1867,46 +1860,105 @@
 		}
 	} while ((newslave = newslave->next) != oldslave);
 
-	/* no usable backup found, we'll see if we at least got a link that was
-	   coming back for a long time, and could possibly already be usable.
-	*/
-
-	if (bestslave != NULL) {
-		/* early take-over. */
-		printk (" and making interface %s the active one %d ms earlier.\n",
-			bestslave->dev->name,
-			(updelay - bestslave->delay)*miimon);
-
-		bestslave->delay = 0;
-		bestslave->link = BOND_LINK_UP;
-		bestslave->jiffies = jiffies;
-		bond_set_slave_active_flags(bestslave);
-		bond_mc_update(bond, bestslave, oldslave);
-		bond_assign_current_slave(bond, bestslave);
-		return bestslave;
-	}
-
-	if ((bond_mode == BOND_MODE_ACTIVEBACKUP) &&
-	    (multicast_mode == BOND_MULTICAST_ACTIVE) &&
-	    (oldslave != NULL)) {
-		/* flush bonds (master's) mc_list from oldslave since it wasn't
-		 * updated (and deleted) above
-		 */ 
-		bond_mc_list_flush(oldslave->dev, bond->device); 
-		if (bond->device->flags & IFF_PROMISC) {
-			dev_set_promiscuity(oldslave->dev, -1);
+	return bestslave;
+}
+
+/**
+ * change_active_interface - change the active slave into the specified one
+ * @bond: our bonding struct
+ * @new: the new slave to make the active one
+ * 
+ * Set the new slave to the bond's settings and unset them on the old
+ * current_slave.
+ * Setting include flags, mc-list, promiscuity, allmulti, etc.
+ *
+ * If @new's link state is %BOND_LINK_BACK we'll set it to %BOND_LINK_UP,
+ * because it is apparently the best available slave we have, even though its
+ * updelay hasn't timed out yet.
+ *
+ * Warning: Caller must hold ptrlock for writing.
+ */
+static void change_active_interface(struct bonding *bond, struct slave *new)
+{
+	struct slave *old = bond->current_slave;
+
+	if (old == new) {
+		return;
+	}
+
+	if (new) {
+		if (new->link == BOND_LINK_BACK) {
+			if (USES_PRIMARY(bond_mode)) {
+				printk (KERN_INFO
+					"%s: making interface %s the new "
+					"active one %d ms earlier.\n",
+					bond->device->name, new->dev->name,
+					(updelay - new->delay) * miimon);
+			}
+
+			new->delay = 0;
+			new->link = BOND_LINK_UP;
+			new->jiffies = jiffies;
+
+			if (bond_mode == BOND_MODE_8023AD) {
+				bond_3ad_handle_link_change(new, BOND_LINK_UP);
+			}
+
+			if ((bond_mode == BOND_MODE_TLB) ||
+			    (bond_mode == BOND_MODE_ALB)) {
+				bond_alb_handle_link_change(bond, new, BOND_LINK_UP);
+			}
+		} else {
+			if (USES_PRIMARY(bond_mode)) {
+				printk (KERN_INFO
+					"%s: making interface %s the new active one.\n",
+					bond->device->name, new->dev->name);
+			}
 		}
-		if (bond->device->flags & IFF_ALLMULTI) {
-			dev_set_allmulti(oldslave->dev, -1);
+	}
+
+	if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
+		if (old) {
+			bond_set_slave_inactive_flags(old);
+		}
+
+		if (new) {
+			bond_set_slave_active_flags(new);
 		}
 	}
 
-	printk (" but could not find any %s interface.\n",
-		(bond_mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other");
-	
-	/* absolutely nothing found. let's return NULL */
-	bond_assign_current_slave(bond, NULL);
-	return NULL;
+	if (USES_PRIMARY(bond_mode)) {
+		bond_mc_update(bond, new, old);
+	}
+
+	if ((bond_mode == BOND_MODE_TLB) ||
+	    (bond_mode == BOND_MODE_ALB)) {
+		bond_alb_assign_current_slave(bond, new);
+	} else {
+		bond->current_slave = new;
+	}
+}
+
+/**
+ * reselect_active_interface - select a new active slave, if needed
+ * @bond: our bonding struct
+ *
+ * This functions shoud be called when one of the following occurs:
+ * - The old current_slave has been released or lost its link.
+ * - The primary_slave has got its link back.
+ * - A slave has got its link back and there's no old current_slave.
+ *
+ * Warning: Caller must hold ptrlock for writing.
+ */
+static void reselect_active_interface(struct bonding *bond)
+{
+	struct slave *best_slave;
+
+	best_slave = find_best_interface(bond);
+
+	if (best_slave != bond->current_slave) {
+		change_active_interface(bond, best_slave);
+	}
 }
 
 /*
@@ -1955,12 +2007,12 @@
 				"of %s to a different address "
 				"to avoid conflicts.\n",
 				       slave->name,
-				       slave->dev_addr[0],
-				       slave->dev_addr[1],
-				       slave->dev_addr[2],
-				       slave->dev_addr[3],
-				       slave->dev_addr[4],
-				       slave->dev_addr[5],
+				       our_slave->perm_hwaddr[0],
+				       our_slave->perm_hwaddr[1],
+				       our_slave->perm_hwaddr[2],
+				       our_slave->perm_hwaddr[3],
+				       our_slave->perm_hwaddr[4],
+				       our_slave->perm_hwaddr[5],
 				       bond->device->name,
 				       slave->name);
 			}
@@ -1973,6 +2025,11 @@
 				bond_3ad_unbind_slave(our_slave);
 			}
 
+			printk (KERN_INFO "%s: releasing %s interface %s\n",
+				master->name,
+				(our_slave->state == BOND_STATE_ACTIVE) ? "active" : "backup",
+				slave->name);
+
 			/* release the slave from its bond */
 			bond_detach_slave(bond, our_slave);
 
@@ -1980,18 +2037,11 @@
 				bond->primary_slave = NULL;
 			}
 
-			printk (KERN_INFO "%s: releasing %s interface %s",
-				master->name,
-				(our_slave->state == BOND_STATE_ACTIVE) ? "active" : "backup",
-				slave->name);
-
-			if (our_slave == old_current) {
-				/* find a new interface and be verbose */
-				change_active_interface(bond); 
-			} else {
-				printk(".\n");
+			if (bond->current_slave == our_slave) {
+				change_active_interface(bond, NULL);
+				reselect_active_interface(bond);
 			}
-			
+
 			if (bond->current_slave == NULL) {
 				printk(KERN_INFO
 					"%s: now running without any active interface !\n",
@@ -2019,16 +2069,22 @@
 		return -EINVAL;
 	}
 
+	/* unset promiscuity level from slave */
+	if (master->flags & IFF_PROMISC) {
+		/* If the mode USES_PRIMARY, then we should only remove its
+		 * promisc settings if it was the current_slave, but that was
+		 * already taken care of above when we detached the slave
+		 */
+		if (!USES_PRIMARY(bond_mode)) {
+			dev_set_promiscuity(slave, -1); 
+		}
+	}
+
 	/* undo settings and restore original values */
-	
 	if (multicast_mode == BOND_MULTICAST_ALL) {
 		/* flush master's mc_list from slave */ 
 		bond_mc_list_flush (slave, master); 
 
-		/* unset promiscuity level from slave */
-		if (master->flags & IFF_PROMISC) 
-			dev_set_promiscuity(slave, -1); 
-
 		/* unset allmulti level from slave */ 
 		if (master->flags & IFF_ALLMULTI)
 			dev_set_allmulti(slave, -1); 
@@ -2095,7 +2151,7 @@
 	}
 
 	old_current = bond->current_slave;
-	bond_assign_current_slave(bond, NULL);
+	change_active_interface(bond, NULL);
 	bond->current_arp_slave = NULL;
 	bond->primary_slave = NULL;
 
@@ -2124,17 +2180,17 @@
 		 */
 		write_unlock_bh(&bond->lock);
 
-		if (multicast_mode == BOND_MULTICAST_ALL 
-		    || (multicast_mode == BOND_MULTICAST_ACTIVE 
-			&& old_current == our_slave)) {
+		/* unset promiscuity level from slave */
+		if (master->flags & IFF_PROMISC) {
+			if (!USES_PRIMARY(bond_mode)) {
+				dev_set_promiscuity(slave_dev, -1); 
+			}
+		}
 
+		if (multicast_mode == BOND_MULTICAST_ALL) {
 			/* flush master's mc_list from slave */ 
 			bond_mc_list_flush (slave_dev, master); 
 
-			/* unset promiscuity level from slave */
-			if (master->flags & IFF_PROMISC) 
-				dev_set_promiscuity(slave_dev, -1); 
-
 			/* unset allmulti level from slave */ 
 			if (master->flags & IFF_ALLMULTI)
 				dev_set_allmulti(slave_dev, -1); 
@@ -2183,8 +2239,9 @@
 static void bond_mii_monitor(struct net_device *master)
 {
 	bonding_t *bond = (struct bonding *) master->priv;
-	slave_t *slave, *bestslave, *oldcurrent;
+	slave_t *slave, *oldcurrent;
 	int slave_died = 0;
+	int do_failover = 0;
 
 	read_lock(&bond->lock);
 
@@ -2194,7 +2251,6 @@
 	 * program could monitor the link itself if needed.
 	 */
 
-	bestslave = NULL;
 	slave = (slave_t *)bond;
 
 	read_lock(&bond->ptrlock);
@@ -2202,8 +2258,6 @@
 	read_unlock(&bond->ptrlock);
 
 	while ((slave = slave->prev) != (slave_t *)bond) {
-		/* use updelay+1 to match an UP slave even when updelay is 0 */
-		int mindelay = updelay + 1;
 		struct net_device *dev = slave->dev;
 		int link_state;
 		u16 old_speed = slave->speed;
@@ -2214,14 +2268,7 @@
 		switch (slave->link) {
 		case BOND_LINK_UP:	/* the link was up */
 			if (link_state == BMSR_LSTATUS) {
-				/* link stays up, tell that this one
-				   is immediately available */
-				if (IS_UP(dev) && (mindelay > -2)) {
-					/* -2 is the best case :
-					   this slave was already up */
-					mindelay = -2;
-					bestslave = slave;
-				}
+				/* link stays up, nothing more to do */
 				break;
 			}
 			else { /* link going down */
@@ -2261,6 +2308,7 @@
 					    (bond_mode == BOND_MODE_8023AD)) {
 						bond_set_slave_inactive_flags(slave);
 					}
+
 					printk(KERN_INFO
 						"%s: link status definitely down "
 						"for interface %s, disabling it",
@@ -2277,14 +2325,10 @@
 						bond_alb_handle_link_change(bond, slave, BOND_LINK_DOWN);
 					}
 
-					write_lock(&bond->ptrlock);
-					if (slave == bond->current_slave) {
-						/* find a new interface and be verbose */
-						change_active_interface(bond);
-					} else {
-						printk(".\n");
+					if (slave == oldcurrent) {
+						do_failover = 1;
 					}
-					write_unlock(&bond->ptrlock);
+
 					slave_died = 1;
 				} else {
 					slave->delay--;
@@ -2299,13 +2343,6 @@
 					master->name,
 					(downdelay - slave->delay) * miimon,
 					dev->name);
-
-				if (IS_UP(dev) && (mindelay > -1)) {
-					/* -1 is a good case : this slave went
-					   down only for a short time */
-					mindelay = -1;
-					bestslave = slave;
-				}
 			}
 			break;
 		case BOND_LINK_DOWN:	/* the link was down */
@@ -2375,26 +2412,12 @@
 						bond_alb_handle_link_change(bond, slave, BOND_LINK_UP);
 					}
 
-					write_lock(&bond->ptrlock);
-					if ( (bond->primary_slave != NULL)
-					  && (slave == bond->primary_slave) )
-						change_active_interface(bond); 
-					write_unlock(&bond->ptrlock);
-				}
-				else
+					if ((oldcurrent == NULL) ||
+					    (slave == bond->primary_slave)) {
+						do_failover = 1;
+					}
+				} else {
 					slave->delay--;
-
-				/* we'll also look for the mostly eligible slave */
-				if (bond->primary_slave == NULL)  {
-				    if (IS_UP(dev) && (slave->delay < mindelay)) {
-					mindelay = slave->delay;
-					bestslave = slave;
-				    } 
-				} else if ( (IS_UP(bond->primary_slave->dev))  || 
-				          ( (!IS_UP(bond->primary_slave->dev))  && 
-				          (IS_UP(dev) && (slave->delay < mindelay)) ) ) {
-					mindelay = slave->delay;
-					bestslave = slave;
 				}
 			}
 			break;
@@ -2413,58 +2436,17 @@
 
 	} /* end of while */
 
-	/* 
-	 * if there's no active interface and we discovered that one
-	 * of the slaves could be activated earlier, so we do it.
-	 */
-	read_lock(&bond->ptrlock);
-	oldcurrent = bond->current_slave;
-	read_unlock(&bond->ptrlock);
+	if (do_failover) {
+		write_lock(&bond->ptrlock);
 
-	/* no active interface at the moment or need to bring up the primary */
-	if (oldcurrent == NULL)  { /* no active interface at the moment */
-		if (bestslave != NULL) { /* last chance to find one ? */
-			if (bestslave->link == BOND_LINK_UP) {
-				printk (KERN_INFO
-					"%s: making interface %s the new active one.\n",
-					master->name, bestslave->dev->name);
-			} else {
-				printk (KERN_INFO
-					"%s: making interface %s the new "
-					"active one %d ms earlier.\n",
-					master->name, bestslave->dev->name,
-					(updelay - bestslave->delay) * miimon);
-
-				bestslave->delay = 0;
-				bestslave->link  = BOND_LINK_UP;
-				bestslave->jiffies = jiffies;
-
-				/* notify ad that the link status has changed */
-				if (bond_mode == BOND_MODE_8023AD) {
-					bond_3ad_handle_link_change(bestslave, BOND_LINK_UP);
-				}
-
-				if ((bond_mode == BOND_MODE_TLB) ||
-				    (bond_mode == BOND_MODE_ALB)) {
-					bond_alb_handle_link_change(bond, bestslave, BOND_LINK_UP);
-				}
-			}
-
-			if (bond_mode == BOND_MODE_ACTIVEBACKUP) {
-				bond_set_slave_active_flags(bestslave);
-				bond_mc_update(bond, bestslave, NULL);
-			} else if (bond_mode != BOND_MODE_8023AD) {
-				bestslave->state = BOND_STATE_ACTIVE;
-			}
-			write_lock(&bond->ptrlock);
-			bond_assign_current_slave(bond, bestslave);
-			write_unlock(&bond->ptrlock);
-		} else if (slave_died) {
-			/* print this message only once a slave has just died */
+		reselect_active_interface(bond);
+		if (oldcurrent && !bond->current_slave) {
 			printk(KERN_INFO
 				"%s: now running without any active interface !\n",
 				master->name);
 		}
+
+		write_unlock(&bond->ptrlock);
 	}
 
 	read_unlock(&bond->lock);
@@ -2482,9 +2464,10 @@
 static void loadbalance_arp_monitor(struct net_device *master)
 {
 	bonding_t *bond;
-	slave_t *slave;
+	slave_t *slave, *oldcurrent;
 	int the_delta_in_ticks =  arp_interval * HZ / 1000;
 	int next_timer = jiffies + (arp_interval * HZ / 1000);
+	int do_failover = 0;
 
 	bond = (struct bonding *) master->priv; 
 	if (master->priv == NULL) {
@@ -2508,6 +2491,10 @@
 
 	read_lock(&bond->lock);
 
+	read_lock(&bond->ptrlock);
+	oldcurrent = bond->current_slave;
+	read_unlock(&bond->ptrlock);
+
 	/* see if any of the previous devices are up now (i.e. they have
 	 * xmt and rcv traffic). the current_slave does not come into
 	 * the picture unless it is null. also, slave->jiffies is not needed
@@ -2534,21 +2521,19 @@
 				 * current_slave being null after enslaving
 				 * is closed.
 				 */
-				write_lock(&bond->ptrlock);
-				if (bond->current_slave == NULL) {
+				if (oldcurrent == NULL) {
 					printk(KERN_INFO
 						"%s: link status definitely up "
 						"for interface %s, ",
 						master->name,
 						slave->dev->name);
-					change_active_interface(bond); 
+					do_failover = 1;
 				} else {
 					printk(KERN_INFO
 						"%s: interface %s is now up\n",
 						master->name,
 						slave->dev->name);
 				}
-				write_unlock(&bond->ptrlock);
 			} 
 		} else {
 			/* slave->link == BOND_LINK_UP */
@@ -2571,11 +2556,9 @@
 				       master->name,
 				       slave->dev->name);
 
-				write_lock(&bond->ptrlock);
-				if (slave == bond->current_slave) {
-					change_active_interface(bond);
+				if (slave == oldcurrent) {
+					do_failover = 1;
 				}
-				write_unlock(&bond->ptrlock);
 			}
 		} 
 
@@ -2591,6 +2574,19 @@
 		}
 	}
 
+	if (do_failover) {
+		write_lock(&bond->ptrlock);
+
+		reselect_active_interface(bond);
+		if (oldcurrent && !bond->current_slave) {
+			printk(KERN_INFO
+				"%s: now running without any active interface !\n",
+				master->name);
+		}
+
+		write_unlock(&bond->ptrlock);
+	}
+
 	read_unlock(&bond->lock);
 	rtnl_exunlock();
 	rtnl_shunlock();
@@ -2651,9 +2647,7 @@
 				if ((bond->current_slave == NULL) &&
 				    ((jiffies - slave->dev->trans_start) <=
 				     the_delta_in_ticks)) {
-					bond_assign_current_slave(bond, slave);
-					bond_set_slave_active_flags(slave);
-			                bond_mc_update(bond, slave, NULL);
+					change_active_interface(bond, slave);
 					bond->current_arp_slave = NULL;
 				} else if (bond->current_slave != slave) {
 					/* this slave has just come up but we 
@@ -2743,7 +2737,8 @@
 			       master->name,
 			       slave->dev->name);
 			write_lock(&bond->ptrlock);
-			slave = change_active_interface(bond);
+			reselect_active_interface(bond);
+			slave = bond->current_slave;
 			write_unlock(&bond->ptrlock);
 			bond->current_arp_slave = slave;
 			if (slave != NULL) {
@@ -2762,13 +2757,10 @@
 			       bond->primary_slave->dev->name);
 			       
 			/* primary is up so switch to it */
-			bond_set_slave_inactive_flags(slave);
-			bond_mc_update(bond, bond->primary_slave, slave);
 			write_lock(&bond->ptrlock);
-			bond_assign_current_slave(bond, bond->primary_slave);
+			change_active_interface(bond, bond->primary_slave);
 			write_unlock(&bond->ptrlock);
 			slave = bond->primary_slave;
-			bond_set_slave_active_flags(slave);
 			slave->jiffies = jiffies;
 		} else {
 			bond->current_arp_slave = NULL;
@@ -2811,7 +2803,7 @@
 				/* if the link state is up at this point, we 
 				 * mark it down - this can happen if we have 
 				 * simultaneous link failures and 
-				 * change_active_interface doesn't make this 
+				 * reselect_active_interface doesn't make this 
 				 * one the current slave so it is still marked 
 				 * up when it is actually down
 				 */
@@ -3057,14 +3049,12 @@
 			ret = bond_release(master_dev, slave_dev); 
 			break;
 		case BOND_SETHWADDR_OLD:
-		case SIOCBONDSETHWADDR:	
+		case SIOCBONDSETHWADDR:
 			ret = bond_sethwaddr(master_dev, slave_dev);
 			break;
 		case BOND_CHANGE_ACTIVE_OLD:
 		case SIOCBONDCHANGEACTIVE:
-			if ((bond_mode == BOND_MODE_ACTIVEBACKUP) ||
-			    (bond_mode == BOND_MODE_TLB) ||
-			    (bond_mode == BOND_MODE_ALB)) {
+			if (USES_PRIMARY(bond_mode)) {
 				ret = bond_change_active(master_dev, slave_dev);
 			}
 			else {
@@ -3186,7 +3176,7 @@
 			dev_queue_xmit(skb);
 
 			write_lock(&bond->ptrlock);
-			bond_assign_current_slave(bond, slave->next);
+			bond->current_slave = slave->next;
 			write_unlock(&bond->ptrlock);
 
 			read_unlock(&bond->lock);
@@ -3330,10 +3320,10 @@
 static struct net_device_stats *bond_get_stats(struct net_device *dev)
 {
 	bonding_t *bond = dev->priv;
-	struct net_device_stats *stats = bond->stats, *sstats;
+	struct net_device_stats *stats = &(bond->stats), *sstats;
 	slave_t *slave;
 
-	memset(bond->stats, 0, sizeof(struct net_device_stats));
+	memset(stats, 0, sizeof(struct net_device_stats));
 
 	read_lock_bh(&bond->lock);
 
@@ -3372,215 +3362,590 @@
 	return stats;
 }
 
-static int bond_get_info(char *buf, char **start, off_t offset, int length)
+#ifdef CONFIG_PROC_FS
+
+#define SEQ_START_TOKEN ((void *)1)
+
+static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
 {
-	bonding_t *bond = these_bonds;
-	int len = 0;
-	off_t begin = 0;
-	u16 link;
-	slave_t *slave = NULL;
+	struct bonding *bond = seq->private;
+	loff_t off = 0;
+	struct slave *slave;
 
-	len += sprintf(buf + len, "%s\n", version);
+	/* make sure the bond won't be taken away */
+	read_lock(&dev_base_lock);
+	read_lock_bh(&bond->lock);
 
-	while (bond != NULL) {
-		/*
-		 * This function locks the mutex, so we can't lock it until 
-		 * afterwards
-		 */
-		link = bond_check_mii_link(bond);
+	if (*pos == 0) {
+		return SEQ_START_TOKEN;
+	}
 
-		len += sprintf(buf + len, "Bonding Mode: %s\n",
-			       bond_mode_name());
+	for (slave = bond->prev; slave != (slave_t *)bond;
+	     slave = slave->prev) {
 
-		if ((bond_mode == BOND_MODE_ACTIVEBACKUP) ||
-		    (bond_mode == BOND_MODE_TLB) ||
-		    (bond_mode == BOND_MODE_ALB)) {
-			read_lock_bh(&bond->lock);
-			read_lock(&bond->ptrlock);
-			if (bond->current_slave != NULL) {
-				len += sprintf(buf + len, 
-					"Currently Active Slave: %s\n", 
-					bond->current_slave->dev->name);
-			}
-			read_unlock(&bond->ptrlock);
-			read_unlock_bh(&bond->lock);
-		}
-
-		len += sprintf(buf + len, "MII Status: ");
-		len += sprintf(buf + len, 
-				link == BMSR_LSTATUS ? "up\n" : "down\n");
-		len += sprintf(buf + len, "MII Polling Interval (ms): %d\n", 
-				miimon);
-		len += sprintf(buf + len, "Up Delay (ms): %d\n", 
-				updelay * miimon);
-		len += sprintf(buf + len, "Down Delay (ms): %d\n", 
-				downdelay * miimon);
-		len += sprintf(buf + len, "Multicast Mode: %s\n",
-			       multicast_mode_name());
+		if (++off == *pos) {
+			return slave;
+		}
+	}
 
-		read_lock_bh(&bond->lock);
+	return NULL;
+}
 
-		if (bond_mode == BOND_MODE_8023AD) {
-			struct ad_info ad_info;
+static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct bonding *bond = seq->private;
+	struct slave *slave = v;
 
-			len += sprintf(buf + len, "\n802.3ad info\n");
+	++*pos;
+	if (v == SEQ_START_TOKEN) {
+		slave = bond->prev;
+	} else {
+		slave = slave->prev;
+	}
 
-			if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
-				len += sprintf(buf + len, "bond %s has no active aggregator\n", bond->device->name);
-			} else {
-				len += sprintf(buf + len, "Active Aggregator Info:\n");
+	return (slave == (struct slave *) bond) ? NULL : slave;
+}
 
-				len += sprintf(buf + len, "\tAggregator ID: %d\n", ad_info.aggregator_id);
-				len += sprintf(buf + len, "\tNumber of ports: %d\n", ad_info.ports);
-				len += sprintf(buf + len, "\tActor Key: %d\n", ad_info.actor_key);
-				len += sprintf(buf + len, "\tPartner Key: %d\n", ad_info.partner_key);
-				len += sprintf(buf + len, "\tPartner Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
-					       ad_info.partner_system[0],
-					       ad_info.partner_system[1],
-					       ad_info.partner_system[2],
-					       ad_info.partner_system[3],
-					       ad_info.partner_system[4],
-					       ad_info.partner_system[5]);
-			}
+static void bond_info_seq_stop(struct seq_file *seq, void *v)
+{
+	struct bonding *bond = seq->private;
+
+	read_unlock_bh(&bond->lock);
+	read_unlock(&dev_base_lock);
+}
+
+static void bond_info_show_master(struct seq_file *seq, struct bonding *bond)
+{
+	struct slave *curr;
+
+	read_lock(&bond->ptrlock);
+	curr = bond->current_slave;
+	read_unlock(&bond->ptrlock);
+
+	seq_printf(seq, "Bonding Mode: %s\n", bond_mode_name());
+
+	if (USES_PRIMARY(bond_mode)) {
+		if (curr) {
+			seq_printf(seq,
+				   "Currently Active Slave: %s\n",
+				   curr->dev->name);
 		}
+	}
 
-		for (slave = bond->prev; slave != (slave_t *)bond; 
-		     slave = slave->prev) {
-			len += sprintf(buf + len, "\nSlave Interface: %s\n", slave->dev->name);
+	seq_printf(seq, "MII Status: %s\n", (curr) ? "up" : "down");
+	seq_printf(seq, "MII Polling Interval (ms): %d\n", miimon);
+	seq_printf(seq, "Up Delay (ms): %d\n", updelay * miimon);
+	seq_printf(seq, "Down Delay (ms): %d\n", downdelay * miimon);
+	seq_printf(seq, "Multicast Mode: %s\n", multicast_mode_name());
 
-			len += sprintf(buf + len, "MII Status: ");
+	if (bond_mode == BOND_MODE_8023AD) {
+		struct ad_info ad_info;
 
-			len += sprintf(buf + len, 
-				slave->link == BOND_LINK_UP ? 
-				"up\n" : "down\n");
-			len += sprintf(buf + len, "Link Failure Count: %d\n", 
-				slave->link_failure_count);
-
-			if (app_abi_ver >= 1) {
-				len += sprintf(buf + len,
-					       "Permanent HW addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
-					       slave->perm_hwaddr[0],
-					       slave->perm_hwaddr[1],
-					       slave->perm_hwaddr[2],
-					       slave->perm_hwaddr[3],
-					       slave->perm_hwaddr[4],
-					       slave->perm_hwaddr[5]);
-			}
+		seq_puts(seq, "\n802.3ad info\n");
 
-			if (bond_mode == BOND_MODE_8023AD) {
-				struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
+		if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
+			seq_printf(seq, "bond %s has no active aggregator\n",
+				   bond->device->name);
+		} else {
+			seq_printf(seq, "Active Aggregator Info:\n");
 
-				if (agg) {
-					len += sprintf(buf + len, "Aggregator ID: %d\n",
-						       agg->aggregator_identifier);
-				} else {
-					len += sprintf(buf + len, "Aggregator ID: N/A\n");
-				}
-			}
+			seq_printf(seq, "\tAggregator ID: %d\n",
+				   ad_info.aggregator_id);
+			seq_printf(seq, "\tNumber of ports: %d\n",
+				   ad_info.ports);
+			seq_printf(seq, "\tActor Key: %d\n",
+				   ad_info.actor_key);
+			seq_printf(seq, "\tPartner Key: %d\n",
+				   ad_info.partner_key);
+			seq_printf(seq, "\tPartner Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+				   ad_info.partner_system[0],
+				   ad_info.partner_system[1],
+				   ad_info.partner_system[2],
+				   ad_info.partner_system[3],
+				   ad_info.partner_system[4],
+				   ad_info.partner_system[5]);
 		}
-		read_unlock_bh(&bond->lock);
+	}
+}
 
-		/*
-		 * Figure out the calcs for the /proc/net interface
-		 */
-		*start = buf + (offset - begin);
-		len -= (offset - begin);
-		if (len > length) {
-			len = length;
+static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave)
+{
+	seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
+	seq_printf(seq, "MII Status: %s\n",
+		   (slave->link == BOND_LINK_UP) ?  "up" : "down");
+	seq_printf(seq, "Link Failure Count: %d\n",
+		   slave->link_failure_count);
+
+	if (app_abi_ver >= 1) {
+		seq_printf(seq,
+			   "Permanent HW addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
+			   slave->perm_hwaddr[0],
+			   slave->perm_hwaddr[1],
+			   slave->perm_hwaddr[2],
+			   slave->perm_hwaddr[3],
+			   slave->perm_hwaddr[4],
+			   slave->perm_hwaddr[5]);
+	}
+
+	if (bond_mode == BOND_MODE_8023AD) {
+		const struct aggregator *agg
+			= SLAVE_AD_INFO(slave).port.aggregator;
+
+		if (agg) {
+			seq_printf(seq, "Aggregator ID: %d\n",
+				   agg->aggregator_identifier);
+		} else {
+			seq_puts(seq, "Aggregator ID: N/A\n");
 		}
-		if (len < 0) {
-			len = 0;
+	}
+}
+
+static int bond_info_seq_show(struct seq_file *seq, void *v)
+{
+	if (v == SEQ_START_TOKEN) {
+		seq_printf(seq, "%s\n", version);
+		bond_info_show_master(seq, seq->private);
+	} else {
+		bond_info_show_slave(seq, v);
+	}
+
+	return 0;
+}
+
+static struct seq_operations bond_info_seq_ops = {
+	.start = bond_info_seq_start,
+	.next  = bond_info_seq_next,
+	.stop  = bond_info_seq_stop,
+	.show  = bond_info_seq_show,
+};
+
+static int bond_info_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int rc;
+
+	rc = seq_open(file, &bond_info_seq_ops);
+	if (!rc) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+	return rc;
+}
+
+static struct file_operations bond_info_fops = {
+	.owner	 = THIS_MODULE,
+	.open    = bond_info_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static int bond_create_proc_info(struct bonding *bond)
+{
+	struct net_device *dev = bond->device;
+
+	if (bond_proc_dir) {
+		bond->bond_proc_file = create_proc_entry(dev->name,
+							 S_IRUGO, 
+							 bond_proc_dir);
+		if (bond->bond_proc_file == NULL) {
+			printk(KERN_WARNING
+			       "%s: Cannot create /proc/net/bonding/%s\n", 
+			       dev->name, dev->name);
+		} else {
+			bond->bond_proc_file->data = bond;
+			bond->bond_proc_file->proc_fops = &bond_info_fops;
+			bond->bond_proc_file->owner = THIS_MODULE;
+			memcpy(bond->procdir_name, dev->name, IFNAMSIZ);
 		}
+	}
 
+	return 0;
+}
 
-		bond = bond->next_bond;
+static void bond_destroy_proc_info(struct bonding *bond)
+{
+	if (bond_proc_dir && bond->bond_proc_file) {
+		remove_proc_entry(bond->procdir_name, bond_proc_dir);
+		memset(bond->procdir_name, 0, IFNAMSIZ);
+		bond->bond_proc_file = NULL;
 	}
-	return len;
 }
 
-static int bond_event(struct notifier_block *this, unsigned long event, 
-			void *ptr)
+/* Create the bonding directory under /proc/net, if doesn't exist yet.
+ * Caller must hold rtnl_lock.
+ */
+static void bond_create_proc_dir(void)
 {
-	struct bonding *this_bond = (struct bonding *)these_bonds;
-	struct bonding *last_bond;
-	struct net_device *event_dev = (struct net_device *)ptr;
+	int len = strlen(DRV_NAME);
 
-	/* while there are bonds configured */
-	while (this_bond != NULL) {
-		if (this_bond == event_dev->priv ) {
-			switch (event) {
-			case NETDEV_UNREGISTER:
-				/* 
-				 * remove this bond from a linked list of 
-				 * bonds 
-				 */
-				if (this_bond == these_bonds) {
-					these_bonds = this_bond->next_bond;
-				} else {
-					for (last_bond = these_bonds; 
-					     last_bond != NULL; 
-					     last_bond = last_bond->next_bond) {
-						if (last_bond->next_bond == 
-						    this_bond) {
-							last_bond->next_bond = 
-							this_bond->next_bond;
-						}
-					}
-				}
-				return NOTIFY_DONE;
+	for (bond_proc_dir = proc_net->subdir; bond_proc_dir;
+	     bond_proc_dir = bond_proc_dir->next) {
+		if ((bond_proc_dir->namelen == len) &&
+		    !memcmp(bond_proc_dir->name, DRV_NAME, len)) {
+			break;
+		}
+	}
 
-			default:
-				return NOTIFY_DONE;
-			}
-		} else if (this_bond->device == event_dev->master) {
-			switch (event) {
-			case NETDEV_UNREGISTER:
-				bond_release(this_bond->device, event_dev);
-				break;
-			}
-			return NOTIFY_DONE;
+	if (!bond_proc_dir) {
+		bond_proc_dir = proc_mkdir(DRV_NAME, proc_net);
+		if (bond_proc_dir) {
+			bond_proc_dir->owner = THIS_MODULE;
+		} else {
+			printk(KERN_WARNING DRV_NAME
+				": Warning: cannot create /proc/net/%s\n",
+				DRV_NAME);
+		}
+	}
+}
+
+/* Destroy the bonding directory under /proc/net, if empty.
+ * Caller must hold rtnl_lock.
+ */
+static void bond_destroy_proc_dir(void)
+{
+	struct proc_dir_entry *de;
+
+	if (!bond_proc_dir) {
+		return;
+	}
+
+	/* verify that the /proc dir is empty */
+	for (de = bond_proc_dir->subdir; de; de = de->next) {
+		/* ignore . and .. */
+		if (*(de->name) != '.') {
+			break;
+		}
+	}
+
+	if (de) {
+		if (bond_proc_dir->owner == THIS_MODULE) {
+			bond_proc_dir->owner = NULL;
+		}
+	} else {
+		remove_proc_entry(DRV_NAME, proc_net);
+		bond_proc_dir = NULL;
+	}
+}
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * Change HW address
+ *
+ * Note that many devices must be down to change the HW address, and
+ * downing the master releases all slaves.  We can make bonds full of
+ * bonding devices to test this, however.
+ */
+static inline int
+bond_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct bonding *bond = dev->priv;
+	struct sockaddr *sa = addr, tmp_sa;
+	struct slave *slave;
+	int error;
+
+	dprintk(KERN_INFO "bond_set_mac_address %p %s\n", dev,
+	       dev->name);
+
+	if (!is_valid_ether_addr(sa->sa_data)) {
+		return -EADDRNOTAVAIL;
+	}
+
+	for (slave = bond->prev; slave != (struct slave *)bond;
+	     slave = slave->prev) {
+		dprintk(KERN_INFO "bond_set_mac: slave %p %s\n", slave,
+			slave->dev->name);
+		if (slave->dev->set_mac_address == NULL) {
+			error = -EOPNOTSUPP;
+			dprintk(KERN_INFO "bond_set_mac EOPNOTSUPP %s\n",
+				slave->dev->name);
+			goto unwind;
+		}
+
+		error = slave->dev->set_mac_address(slave->dev, addr);
+		if (error) {
+			/* TODO: consider downing the slave 
+			 * and retry ?
+			 * User should expect communications
+			 * breakage anyway until ARP finish
+			 * updating, so...
+			 */
+			dprintk(KERN_INFO "bond_set_mac err %d %s\n",
+				error, slave->dev->name);
+			goto unwind;
+		}
+	}
+
+	/* success */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	return 0;
+
+unwind:
+	memcpy(tmp_sa.sa_data, dev->dev_addr, dev->addr_len);
+	tmp_sa.sa_family = dev->type;
+
+	for (slave = slave->next; slave != bond->next;
+	     slave = slave->next) {
+		int tmp_error;
+
+		tmp_error = slave->dev->set_mac_address(slave->dev, &tmp_sa);
+		if (tmp_error) {
+			dprintk(KERN_INFO "bond_set_mac_address: "
+				"unwind err %d dev %s\n",
+				tmp_error, slave->dev->name);
+		}
+	}
+
+	return error;
+}
+
+/*
+ * Change the MTU of all of a master's slaves to match the master
+ */
+static inline int
+bond_change_mtu(struct net_device *dev, int newmtu)
+{
+	bonding_t *bond = dev->priv;
+	slave_t *slave;
+	int error;
+
+	dprintk(KERN_INFO "CM: b %p nm %d\n", bond, newmtu);
+	for (slave = bond->prev; slave != (slave_t *)bond;
+	     slave = slave->prev) {
+		dprintk(KERN_INFO "CM: s %p s->p %p c_m %p\n", slave,
+			slave->prev, slave->dev->change_mtu);
+		if (slave->dev->change_mtu) {
+			error = slave->dev->change_mtu(slave->dev, newmtu);
+		} else {
+			slave->dev->mtu = newmtu;
+			error = 0;
+		}
+
+		if (error) {
+			/* If we failed to set the slave's mtu to the new value
+			 * we must abort the operation even in ACTIVE_BACKUP
+			 * mode, because if we allow the backup slaves to have
+			 * different mtu values than the active slave we'll
+			 * need to change their mtu when doing a failover. That
+			 * means changing their mtu from timer context, which
+			 * is probably not a good idea.
+			 */
+			dprintk(KERN_INFO "bond_change_mtu err %d %s\n",
+			       error, slave->dev->name);
+			goto unwind;
+		}
+	}
+
+	dev->mtu = newmtu;
+	return 0;
+
+
+unwind:
+	for (slave = slave->next; slave != bond->next;
+	     slave = slave->next) {
+
+		if (slave->dev->change_mtu) {
+			slave->dev->change_mtu(slave->dev, dev->mtu);
+		} else {
+			slave->dev->mtu = dev->mtu;
+		}
+	}
+
+	return error;
+}
+
+/*
+ * Change device name
+ */
+static inline int bond_event_changename(struct bonding *bond)
+{
+#ifdef CONFIG_PROC_FS
+	bond_destroy_proc_info(bond);
+	bond_create_proc_info(bond);
+#endif
+
+	return NOTIFY_DONE;
+}
+
+static int bond_master_netdev_event(unsigned long event, struct net_device *event_dev)
+{
+	struct bonding *bond, *event_bond = NULL;
+
+	list_for_each_entry(bond, &bond_dev_list, bond_list) {
+		if (bond == event_dev->priv) {
+			event_bond = bond;
+			break;
+		}
+	}
+
+	if (event_bond == NULL) {
+		return NOTIFY_DONE;
+	}
+
+	switch (event) {
+	case NETDEV_CHANGENAME:
+		return bond_event_changename(event_bond);
+	case NETDEV_UNREGISTER:
+		/*
+		 * TODO: remove a bond from the list?
+		 */
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int bond_slave_netdev_event(unsigned long event, struct net_device *event_dev)
+{
+	struct net_device *master = event_dev->master;
+
+	switch (event) {
+	case NETDEV_UNREGISTER:
+		if (master != NULL) {
+			bond_release(master, event_dev);
 		}
-		this_bond = this_bond->next_bond;
+		break;
+	case NETDEV_CHANGE:
+		/*
+		 * TODO: is this what we get if somebody
+		 * sets up a hierarchical bond, then rmmod's
+		 * one of the slave bonding devices?
+		 */
+		break;
+	case NETDEV_DOWN:
+		/*
+		 * ... Or is it this?
+		 */
+		break;
+	case NETDEV_CHANGEMTU:
+		/*
+		 * TODO: Should slaves be allowed to
+		 * independently alter their MTU?  For
+		 * an active-backup bond, slaves need
+		 * not be the same type of device, so
+		 * MTUs may vary.  For other modes,
+		 * slaves arguably should have the
+		 * same MTUs. To do this, we'd need to
+		 * take over the slave's change_mtu
+		 * function for the duration of their
+		 * servitude.
+		 */
+		break;
+	case NETDEV_CHANGENAME:
+		/*
+		 * TODO: handle changing the primary's name
+		 */
+		break;
+	default:
+		break;
 	}
+
 	return NOTIFY_DONE;
 }
 
+/*
+ * bond_netdev_event: handle netdev notifier chain events.
+ *
+ * This function receives events for the netdev chain.  The caller (an
+ * ioctl handler calling notifier_call_chain) holds the necessary
+ * locks for us to safely manipulate the slave devices (RTNL lock,
+ * dev_probe_lock).
+ */
+static int bond_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct net_device *event_dev = (struct net_device *)ptr;
+	unsigned short flags;
+	int res = NOTIFY_DONE;
+
+	dprintk(KERN_INFO "bond_netdev_event n_b %p ev %lx ptr %p\n",
+		this, event, ptr);
+
+	flags = event_dev->flags & (IFF_MASTER | IFF_SLAVE);
+	switch (flags) {
+	case IFF_MASTER:
+		res = bond_master_netdev_event(event, event_dev);
+		break;
+	case IFF_SLAVE:
+		res = bond_slave_netdev_event(event, event_dev);
+		break;
+	default:
+		/* A master that is also a slave ? */
+		break;
+	}
+
+	return res;
+}
+
 static struct notifier_block bond_netdev_notifier = {
-	notifier_call: bond_event,
+	.notifier_call = bond_netdev_event,
 };
 
+/* De-initialize device specific data.
+ * Caller must hold rtnl_lock.
+ */
+static inline void bond_deinit(struct net_device *dev)
+{
+	struct bonding *bond = dev->priv;
+
+	list_del(&bond->bond_list);
+
+#ifdef CONFIG_PROC_FS
+	bond_destroy_proc_info(bond);
+#endif
+}
+
+/* Unregister and free all bond devices.
+ * Caller must hold rtnl_lock.
+ */
+static void bond_free_all(void)
+{
+	struct bonding *bond, *nxt;
+
+	list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
+		struct net_device *dev = bond->device;
+
+		unregister_netdevice(dev);
+		bond_deinit(dev);
+		free_netdev(dev);
+	}
+
+#ifdef CONFIG_PROC_FS
+	bond_destroy_proc_dir();
+#endif
+}
+
+/*
+ * Does not allocate but creates a /proc entry.
+ * Allowed to fail.
+ */
 static int __init bond_init(struct net_device *dev)
 {
-	bonding_t *bond, *this_bond, *last_bond;
+	struct bonding *bond;
 	int count;
 
 #ifdef BONDING_DEBUG
 	printk (KERN_INFO "Begin bond_init for %s\n", dev->name);
 #endif
-	bond = kmalloc(sizeof(struct bonding), GFP_KERNEL);
-	if (bond == NULL) {
-		return -ENOMEM;
-	}
-	memset(bond, 0, sizeof(struct bonding));
+	bond = dev->priv;
 
 	/* initialize rwlocks */
 	rwlock_init(&bond->lock);
 	rwlock_init(&bond->ptrlock);
-	
-	bond->stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
-	if (bond->stats == NULL) {
-		kfree(bond);
-		return -ENOMEM;
-	}
-	memset(bond->stats, 0, sizeof(struct net_device_stats));
 
+	/* Initialize pointers */
 	bond->next = bond->prev = (slave_t *)bond;
 	bond->current_slave = NULL;
 	bond->current_arp_slave = NULL;
 	bond->device = dev;
-	dev->priv = bond;
 
 	/* Initialize the device structure. */
+	dev->set_mac_address = bond_set_mac_address;
+
 	switch (bond_mode) {
 	case BOND_MODE_ACTIVEBACKUP:
 		dev->hard_start_xmit = bond_xmit_activebackup;
@@ -3600,11 +3965,10 @@
 	case BOND_MODE_TLB:
 	case BOND_MODE_ALB:
 		dev->hard_start_xmit = bond_alb_xmit;
+		dev->set_mac_address = bond_alb_set_mac_address;
 		break;
 	default:
 		printk(KERN_ERR "Unknown bonding mode %d\n", bond_mode);
-		kfree(bond->stats);
-		kfree(bond);
 		return -EINVAL;
 	}
 
@@ -3613,15 +3977,7 @@
 	dev->stop = bond_close;
 	dev->set_multicast_list = set_multicast_list;
 	dev->do_ioctl = bond_ioctl;
-
-	/* 
-	 * Fill in the fields of the device structure with ethernet-generic 
-	 * values. 
-	 */
-
-	ether_setup(dev);
-
-	dev->set_mac_address = bond_set_mac_address;
+	dev->change_mtu = bond_change_mtu;
 	dev->tx_queue_len = 0;
 	dev->flags |= IFF_MASTER|IFF_MULTICAST;
 #ifdef CONFIG_NET_FASTROUTE
@@ -3648,42 +4004,12 @@
 	} else {
 		printk("out ARP monitoring\n");
 	}
-
+ 
 #ifdef CONFIG_PROC_FS
-	bond->bond_proc_dir = proc_mkdir(dev->name, proc_net);
-	if (bond->bond_proc_dir == NULL) {
-		printk(KERN_ERR "%s: Cannot init /proc/net/%s/\n", 
-			dev->name, dev->name);
-		kfree(bond->stats);
-		kfree(bond);
-		return -ENOMEM;
-	}
-	bond->bond_proc_info_file = 
-		create_proc_info_entry("info", 0, bond->bond_proc_dir, 
-					bond_get_info);
-	if (bond->bond_proc_info_file == NULL) {
-		printk(KERN_ERR "%s: Cannot init /proc/net/%s/info\n", 
-			dev->name, dev->name);
-		remove_proc_entry(dev->name, proc_net);
-		kfree(bond->stats);
-		kfree(bond);
-		return -ENOMEM;
-	}
-#endif /* CONFIG_PROC_FS */
+	bond_create_proc_info(bond);
+#endif
 
-	if (first_pass == 1) {
-		these_bonds = bond;
-		register_netdevice_notifier(&bond_netdev_notifier);
-		first_pass = 0;
-	} else {
-		last_bond = these_bonds;
-		this_bond = these_bonds->next_bond;
-		while (this_bond != NULL) {
-			last_bond = this_bond;
-			this_bond = this_bond->next_bond;
-		}
-		last_bond->next_bond = bond;
-	} 
+	list_add_tail(&bond->bond_list, &bond_dev_list);
 
 	return 0;
 }
@@ -3723,9 +4049,6 @@
 	int no;
 	int err;
 
-	/* Find a name for this unit */
-	static struct net_device *dev_bond = NULL;
-
 	printk(KERN_INFO "%s", version);
 
 	/*
@@ -3741,6 +4064,12 @@
 		}
 	}
 
+	if (USES_PRIMARY(bond_mode)) {
+		multicast_mode = BOND_MULTICAST_ACTIVE;
+	} else {
+		multicast_mode = BOND_MULTICAST_ALL;
+	}
+
 	if (multicast) {
 		multicast_mode = bond_parse_parm(multicast, bond_mc_tbl);
 		if (multicast_mode == -1) {
@@ -3776,12 +4105,6 @@
 		       max_bonds, 1, INT_MAX, BOND_DEFAULT_MAX_BONDS);
 		max_bonds = BOND_DEFAULT_MAX_BONDS;
 	}
-	dev_bond = dev_bonds = kmalloc(max_bonds*sizeof(struct net_device), 
-					GFP_KERNEL);
-	if (dev_bond == NULL) {
-		return -ENOMEM;
-	}
-	memset(dev_bonds, 0, max_bonds*sizeof(struct net_device));
 
 	if (miimon < 0) {
 		printk(KERN_WARNING 
@@ -3959,9 +4282,7 @@
 		       "link failures! see bonding.txt for details.\n");
 	}
 
-	if ((primary != NULL) && (bond_mode != BOND_MODE_ACTIVEBACKUP) &&
-	    (bond_mode != BOND_MODE_TLB) &&
-	    (bond_mode != BOND_MODE_ALB)){
+	if ((primary != NULL) && !USES_PRIMARY(bond_mode)) {
 		/* currently, using a primary only makes sense
 		 * in active backup, TLB or ALB modes
 		 */
@@ -3972,48 +4293,69 @@
 		primary = NULL;
 	}
 
+	rtnl_lock();
+
+#ifdef CONFIG_PROC_FS
+	bond_create_proc_dir();
+#endif
 
+	err = 0;
 	for (no = 0; no < max_bonds; no++) {
-		dev_bond->init = bond_init;
-	
-		err = dev_alloc_name(dev_bond,"bond%d");
+		struct net_device *dev;
+
+		dev = alloc_netdev(sizeof(struct bonding), "", ether_setup);
+		if (!dev) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+
+		err = dev_alloc_name(dev, "bond%d");
+		if (err < 0) {
+			free_netdev(dev);
+			goto out_err;
+		}
+
+		/* bond_init() must be called after dev_alloc_name() (for the
+		 * /proc files), but before register_netdevice(), because we
+		 * need to set function pointers.
+		 */
+		err = bond_init(dev);
 		if (err < 0) {
-			kfree(dev_bonds);
-			return err;
+			free_netdev(dev);
+			goto out_err;
+		}
+
+		SET_MODULE_OWNER(dev);
+
+		err = register_netdevice(dev);
+		if (err < 0) {
+			bond_deinit(dev);
+			free_netdev(dev);
+			goto out_err;
 		}
-		SET_MODULE_OWNER(dev_bond);
-		if (register_netdev(dev_bond) != 0) {
-			kfree(dev_bonds);
-			return -EIO;
-		}	
-		dev_bond++;
 	}
+
+	rtnl_unlock();
+	register_netdevice_notifier(&bond_netdev_notifier);
+
 	return 0;
+
+out_err:
+	/* free and unregister all bonds that were successfully added */
+	bond_free_all();
+
+	rtnl_unlock();
+
+	return err;
 }
 
 static void __exit bonding_exit(void)
 {
-	struct net_device *dev_bond = dev_bonds;
-	struct bonding *bond;
-	int no;
-
 	unregister_netdevice_notifier(&bond_netdev_notifier);
-		 
-	for (no = 0; no < max_bonds; no++) {
 
-#ifdef CONFIG_PROC_FS
-		bond = (struct bonding *) dev_bond->priv;
-		remove_proc_entry("info", bond->bond_proc_dir);
-		remove_proc_entry(dev_bond->name, proc_net);
-#endif
-		unregister_netdev(dev_bond);
-		kfree(bond->stats);
-		kfree(dev_bond->priv);
-		
-		dev_bond->priv = NULL;
-		dev_bond++;
-	}
-	kfree(dev_bonds);
+	rtnl_lock();
+	bond_free_all();
+	rtnl_unlock();
 }
 
 module_init(bonding_init);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)