patch-2.3.49 linux/drivers/net/bonding.c

Next file: linux/drivers/net/fc/iph5526.c
Previous file: linux/drivers/net/arlan.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.48/linux/drivers/net/bonding.c linux/drivers/net/bonding.c
@@ -0,0 +1,350 @@
+/*
+ * originally based on the dummy device.
+ *
+ * Copyright 1999, Thomas Davis, tadavis@lbl.gov.  
+ * Licensed under the GPL. Based on dummy.c, and eql.c devices.
+ *
+ * bond.c: a bonding/etherchannel/sun trunking net driver
+ *
+ * This is useful to talk to a Cisco 5500, running Etherchannel, aka:
+ *	Linux Channel Bonding
+ *	Sun Trunking (Solaris)
+ *
+ * How it works:
+ *    ifconfig bond0 ipaddress netmask up
+ *      will setup a network device, with an ip address.  No mac address 
+ *	will be assigned at this time.  The hw mac address will come from 
+ *	the first slave bonded to the channel.  All slaves will then use 
+ *	this hw mac address.
+ *
+ *    ifconfig bond0 down
+ *         will release all slaves, marking them as down.
+ *
+ *    ifenslave bond0 eth0
+ *	will attache eth0 to bond0 as a slave.  eth0 hw mac address will either
+ *	a: be used as initial mac address
+ *	b: if a hw mac address already is there, eth0's hw mac address 
+ *	   will then  be set from bond0.
+ *
+ * v0.1 - first working version.
+ * v0.2 - changed stats to be calculated by summing slaves stats.
+ * 
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/if_bonding.h>
+
+typedef struct slave
+{
+	struct slave *next;
+	struct slave *prev;
+	struct net_device *dev;
+} slave_t;
+
+typedef struct bonding
+{
+	slave_t *next;
+	slave_t *prev;
+	struct net_device *master;
+
+	slave_t *current_slave;
+	struct net_device_stats stats;
+} bonding_t;
+
+
+static int bond_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *bond_get_stats(struct net_device *dev);
+
+static struct net_device *this_bond;
+
+static int bond_open(struct net_device *dev)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int bond_close(struct net_device *master)
+{
+	bonding_t *bond = master->priv;
+	slave_t *slave;
+
+	while ((slave = bond->next) != (slave_t*)bond) {
+
+		spin_lock_bh(&master->xmit_lock);
+		slave->next->prev = slave->prev;
+		slave->prev->next = slave->next;
+		bond->current_slave = (slave_t*)bond;
+		spin_unlock_bh(&master->xmit_lock);
+
+		netdev_set_master(slave->dev, NULL);
+
+		kfree(slave);
+	}
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* Fake multicast ability.
+
+   NB. It is possible and necessary to make it true one, otherwise
+   the device is not functional.
+ */
+static void bond_set_multicast_list(struct net_device *dev)
+{
+}
+
+static int bond_enslave(struct net_device *master, struct net_device *dev)
+{
+	int err;
+	bonding_t *bond = master->priv;
+	slave_t *slave;
+
+	if (dev->type != master->type)
+		return -ENODEV;
+
+	if ((slave = kmalloc(sizeof(slave_t), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	memset(slave, 0, sizeof(slave_t));
+
+	err = netdev_set_master(dev, master);
+	if (err) {
+		kfree(slave);
+		return err;
+	}
+
+	slave->dev = dev;
+
+	spin_lock_bh(&master->xmit_lock);
+
+	dev_hold(dev);
+
+	slave->prev = bond->prev;
+	slave->next = (slave_t*)bond;
+	slave->prev->next = slave;
+	slave->next->prev = slave;
+
+	spin_unlock_bh(&master->xmit_lock);
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int bond_release(struct net_device *master, struct net_device *dev)
+{
+	bonding_t *bond = master->priv;
+	slave_t *slave;
+
+	if (dev->master != master)
+		return -EINVAL;
+
+	netdev_set_master(dev, NULL);
+
+	for (slave = bond->next; slave != (slave_t*)bond; slave = slave->next) {
+		if (slave->dev == dev) {
+			spin_lock_bh(&master->xmit_lock);
+			if (bond->current_slave == slave)
+				bond->current_slave = slave->next;
+			slave->next->prev = slave->prev;
+			slave->prev->next = slave->next;
+			spin_unlock_bh(&master->xmit_lock);
+
+			kfree(slave);
+			dev_put(dev);
+			MOD_DEC_USE_COUNT;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/* It is pretty silly, SIOCSIFHWADDR exists to make this. */
+
+static int bond_sethwaddr(struct net_device *master, struct net_device *slave)
+{
+	memcpy(master->dev_addr, slave->dev_addr, slave->addr_len);
+	return 0;
+}
+
+static int bond_ioctl(struct net_device *master, struct ifreq *ifr, int cmd)
+{
+	struct net_device *slave = __dev_get_by_name(ifr->ifr_slave);
+
+	if (slave == NULL)
+		return -ENODEV;
+
+	switch (cmd) {
+	case BOND_ENSLAVE:
+		return bond_enslave(master, slave);
+	case BOND_RELEASE:
+		return bond_release(master, slave);
+	case BOND_SETHWADDR:
+		return bond_sethwaddr(master, slave);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int bond_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct net_device *slave = ptr;
+
+	if (this_bond == NULL ||
+	    this_bond == slave ||
+	    this_bond != slave->master)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UNREGISTER:
+		bond_release(this_bond, slave);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+struct notifier_block bond_netdev_notifier={
+	bond_event,
+	NULL,
+	0
+};
+
+int __init bond_init(struct net_device *dev)
+{
+	bonding_t *bond;
+
+	bond = kmalloc(sizeof(struct bonding), GFP_KERNEL);
+	if (bond == NULL)
+		return -ENOMEM;
+
+	memset(bond, 0, sizeof(struct bonding));
+	bond->next = (slave_t*)bond;
+	bond->prev = (slave_t*)bond;
+	bond->master = dev;
+	bond->current_slave = (slave_t*)bond;
+	dev->priv = bond;
+
+	/* Initialize the device structure. */
+	dev->hard_start_xmit = bond_xmit;
+	dev->get_stats	= bond_get_stats;
+	dev->open = bond_open;
+	dev->stop = bond_close;
+	dev->set_multicast_list = bond_set_multicast_list;
+	dev->do_ioctl = bond_ioctl;
+
+	/* Fill in the fields of the device structure with ethernet-generic 
+	   values. */
+	ether_setup(dev);
+	dev->tx_queue_len = 0;
+	dev->flags |= IFF_MASTER;
+
+	this_bond = dev;
+
+	register_netdevice_notifier(&bond_netdev_notifier);
+
+	return 0;
+}
+
+static int bond_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	bonding_t *bond = dev->priv;
+	slave_t *slave, *start_at;
+	int pkt_len = skb->len;
+
+	slave = start_at = bond->current_slave;
+
+	do {
+		if (slave == (slave_t*)bond)
+			continue;
+
+		if (netif_running(slave->dev) && netif_carrier_ok(dev)) {
+			bond->current_slave = slave->next;
+			skb->dev = slave->dev;
+
+			if (dev_queue_xmit(skb)) {
+				bond->stats.tx_dropped++;
+			} else {
+				bond->stats.tx_packets++;
+				bond->stats.tx_bytes += pkt_len;
+			}
+			return 0;
+		}
+	} while ((slave = slave->next) != start_at);
+
+	bond->stats.tx_dropped++;
+	kfree_skb(skb);
+	return 0;
+}
+
+static struct net_device_stats *bond_get_stats(struct net_device *dev)
+{
+	bonding_t *bond = dev->priv;
+
+	return &bond->stats;
+}
+
+#ifdef MODULE
+
+static char bond_name[16];
+
+static struct net_device dev_bond = {
+		bond_name, 	/* Needs to be writeable */
+		0, 0, 0, 0,
+	 	0x0, 0,
+	 	0, 0, 0, NULL, bond_init };
+
+int init_module(void)
+{
+	/* Find a name for this unit */
+	int err=dev_alloc_name(&dev_bond,"bond%d");
+
+	if (err<0)
+		return err;
+
+	if (register_netdev(&dev_bond) != 0)
+		return -EIO;
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unregister_netdevice_notifier(&bond_netdev_notifier);
+
+	unregister_netdev(&dev_bond);
+
+	kfree(dev_bond.priv);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */

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