Subject: [PATCH 5/5] [bonding] backport 2.6 changes to 2.4

From: Amir Noam <[email-masked]>
Date: Wed, 30 Jul 2003 20:07:27 +0300

Backported from 2.6:
Use a linked list to handle numerous bond devices instead of a static
array.

Also, fix handling of errors in bonding_init(): gracefully unregister
and deallocate all previously created bond devices.

Amir

diff -Nuarp linux-2.4.22-pre9/drivers/net/bonding/bond_main.c linux-2.4.22-pre9-devel/drivers/net/bonding/bond_main.c
--- linux-2.4.22-pre9/drivers/net/bonding/bond_main.c	Wed Jul 30 19:14:10 2003
+++ linux-2.4.22-pre9-devel/drivers/net/bonding/bond_main.c	Wed Jul 30 19:14:11 2003
@@ -515,9 +515,7 @@ static struct bond_parm_tbl bond_lacp_tb
 {	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);
 
 MODULE_PARM(max_bonds, "i");
 MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
@@ -3495,47 +3493,13 @@ static int bond_read_proc(char *buf, cha
 static int bond_event(struct notifier_block *this, unsigned long event, 
 			void *ptr)
 {
-	struct bonding *this_bond = (struct bonding *)these_bonds;
-	struct bonding *last_bond;
 	struct net_device *event_dev = (struct net_device *)ptr;
+	struct net_device *master = event_dev->master;
 
-	/* 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;
-
-			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;
-		}
-		this_bond = this_bond->next_bond;
+	if ((event == NETDEV_UNREGISTER) && (master != NULL)) {
+		bond_release(master, event_dev);
 	}
+
 	return NOTIFY_DONE;
 }
 
@@ -3543,19 +3507,40 @@ static struct notifier_block bond_netdev
 	notifier_call: bond_event,
 };
 
+static void bond_deinit(struct net_device *dev)
+{
+	struct bonding *bond = dev->priv;
+
+	list_del(&bond->bond_list);
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("info", bond->bond_proc_dir);
+	remove_proc_entry(dev->name, proc_net);
+#endif
+}
+
+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;
+
+		bond_deinit(dev);
+		unregister_netdev(dev);
+		kfree(dev);
+	}
+}
+
 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);
@@ -3565,7 +3550,6 @@ static int __init bond_init(struct net_d
 	bond->current_slave = NULL;
 	bond->current_arp_slave = NULL;
 	bond->device = dev;
-	dev->priv = bond;
 
 	/* Initialize the device structure. */
 	switch (bond_mode) {
@@ -3590,7 +3574,6 @@ static int __init bond_init(struct net_d
 		break;
 	default:
 		printk(KERN_ERR "Unknown bonding mode %d\n", bond_mode);
-		kfree(bond);
 		return -EINVAL;
 	}
 
@@ -3599,14 +3582,6 @@ static int __init bond_init(struct net_d
 	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->tx_queue_len = 0;
 	dev->flags |= IFF_MASTER|IFF_MULTICAST;
@@ -3640,7 +3615,6 @@ static int __init bond_init(struct net_d
 	if (bond->bond_proc_dir == NULL) {
 		printk(KERN_ERR "%s: Cannot init /proc/net/%s/\n", 
 			dev->name, dev->name);
-		kfree(bond);
 		return -ENOMEM;
 	}
 	bond->bond_proc_dir->owner = THIS_MODULE;
@@ -3652,25 +3626,12 @@ static int __init bond_init(struct net_d
 		printk(KERN_ERR "%s: Cannot init /proc/net/%s/info\n", 
 			dev->name, dev->name);
 		remove_proc_entry(dev->name, proc_net);
-		kfree(bond);
 		return -ENOMEM;
 	}
 	bond->bond_proc_info_file->owner = THIS_MODULE;
 #endif /* CONFIG_PROC_FS */
 
-	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;
 }
@@ -3710,9 +3671,6 @@ static int __init bonding_init(void)
 	int no;
 	int err;
 
-	/* Find a name for this unit */
-	static struct net_device *dev_bond = NULL;
-
 	printk(KERN_INFO "%s", version);
 
 	/*
@@ -3763,12 +3721,6 @@ static int __init bonding_init(void)
 		       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,47 +3911,62 @@ static int __init bonding_init(void)
 		primary = NULL;
 	}
 
+	rtnl_lock();
 
+	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) {
+			kfree(dev);
+			goto out_err;
+		}
+
+		/* bond_init() must be called after alloc_net_dev() (for the
+		 * /proc files), but before register_netdevice(), because we
+		 * need to set function pointers.
+		 */
+		err = bond_init(dev);
+		if (err < 0) {
+			kfree(dev);
+			goto out_err;
+		}
+
+		SET_MODULE_OWNER(dev);
+
+		err = register_netdevice(dev);
 		if (err < 0) {
-			kfree(dev_bonds);
-			return err;
+			bond_deinit(dev);
+			kfree(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:
+	rtnl_unlock();
+
+	/* free and unregister all bonds that were successfully added */
+	bond_free_all();
+
+	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(dev_bond->priv);
-
-		dev_bond->priv = NULL;
-		dev_bond++;
-	}
-	kfree(dev_bonds);
+	bond_free_all();
 }
 
 module_init(bonding_init);
diff -Nuarp linux-2.4.22-pre9/drivers/net/bonding/bonding.h linux-2.4.22-pre9-devel/drivers/net/bonding/bonding.h
--- linux-2.4.22-pre9/drivers/net/bonding/bonding.h	Wed Jul 30 19:14:10 2003
+++ linux-2.4.22-pre9-devel/drivers/net/bonding/bonding.h	Wed Jul 30 19:14:11 2003
@@ -104,7 +104,7 @@ typedef struct bonding {
 	struct proc_dir_entry *bond_proc_dir;
 	struct proc_dir_entry *bond_proc_info_file;
 #endif /* CONFIG_PROC_FS */
-	struct bonding *next_bond;
+	struct list_head bond_list;
 	struct net_device *device;
 	struct dev_mc_list *mc_list;
 	unsigned short flags;