eql-1.2.ds1/0000755000175000017500000000000011224365252011701 5ustar roverrovereql-1.2.ds1/NO-WARRANTY0000644000175000017500000000030605737154051013411 0ustar roverroverIf this destroys your system due to oversight or operator error, neighter NCM or Simon Janes can be held responsible. Legalities.. etc.. This is free software, and is distributed under the GPL. eql-1.2.ds1/eql-1.2.patch0000644000175000017500000016156205737214713014023 0ustar roverroverdiff -u --recursive --new-file linux-1.2.2/arch/i386/config.in linux/arch/i386/config.in --- linux-1.2.2/arch/i386/config.in Tue Mar 7 09:25:26 1995 +++ linux/arch/i386/config.in Fri Mar 31 23:05:10 1995 @@ -119,6 +119,7 @@ fi bool 'PPP (point-to-point) support' CONFIG_PPP n bool 'PLIP (parallel port) support' CONFIG_PLIP n +bool 'EQL (serial line load balancing) support' CONFIG_EQUALIZER y bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then diff -u --recursive --new-file linux-1.2.2/drivers/net/Makefile linux/drivers/net/Makefile --- linux-1.2.2/drivers/net/Makefile Thu Feb 23 06:51:27 1995 +++ linux/drivers/net/Makefile Fri Mar 31 23:05:10 1995 @@ -254,6 +254,10 @@ MODULES := 8390.o $(MODULES) endif +ifdef CONFIG_EQUALIZER +NETDRV_OBJS := $(NETDRV_OBJS) eql.o +endif + net.a: $(NETDRV_OBJS) rm -f net.a $(AR) rcs net.a $(NETDRV_OBJS) diff -u --recursive --new-file linux-1.2.2/drivers/net/Space.c linux/drivers/net/Space.c --- linux-1.2.2/drivers/net/Space.c Sun Mar 26 02:49:58 1995 +++ linux/drivers/net/Space.c Fri Mar 31 23:05:10 1995 @@ -309,6 +309,22 @@ # define NEXT_DEV (&dummy_dev) #endif +#ifdef CONFIG_EQUALIZER +extern int eql_init(struct device *dev); +struct device eql_dev = { + "eql", /* Master device for IP traffic load + balancing */ + 0x0, 0x0, 0x0, 0x0, /* recv end/start; mem end/start */ + 0, /* base I/O address */ + 0, /* IRQ */ + 0, 0, 0, /* flags */ + NEXT_DEV, /* next device */ + eql_init /* set up the rest */ +}; +# undef NEXT_DEV +# define NEXT_DEV (&eql_dev) +#endif + extern int loopback_init(struct device *dev); struct device loopback_dev = { "lo", /* Software Loopback interface */ diff -u --recursive --new-file linux-1.2.2/drivers/net/eql.c linux/drivers/net/eql.c --- linux-1.2.2/drivers/net/eql.c Wed Dec 31 19:00:00 1969 +++ linux/drivers/net/eql.c Fri Mar 31 23:15:11 1995 @@ -0,0 +1,1168 @@ +/* + * Equalizer Load-balancer for serial network interfaces. + * + * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes + * NCM: Network and Communications Mangement, Inc. + * + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * The author may be reached as simon@ncm.com, or C/O + * NCM + * Attn: Simon Janes + * 6803 Whittier Ave + * McLean VA 22101 + * Phone: 1-703-847-0040 ext 103 + */ + +static char *version = + "EQL Multilink Driver: $Revision: 1.2 $ $Date: 1995/03/25 18:16:32 $ Simon Janes (simon@ncm.com)\n"; + +#include + +/* + * Sources: + * skeleton.c by Donald Becker. + * Inspirations: + * The Harried and Overworked Alan Cox + * Conspiracies: + * The Alan Cox and Arisian plot to get someone else to do the code, which + * turned out to be me. + */ + +/* + * $Log: eql.c,v $ + * Revision 1.2 1995/03/25 18:16:32 guru + * Will have to make this the 4.0 branch + * + * Revision 1.1 1995/03/25 18:14:05 guru + * Initial revision + * + * Revision 3.11 1995/01/19 23:14:31 guru + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued * 8; + * + * Revision 3.10 1995/01/19 23:07:53 guru + * back to + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued; + * + * Revision 3.9 1995/01/19 22:38:20 guru + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued * 4; + * + * Revision 3.8 1995/01/19 22:30:55 guru + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued * 2; + * + * Revision 3.7 1995/01/19 21:52:35 guru + * printk's trimmed out. + * + * Revision 3.6 1995/01/19 21:49:56 guru + * This is working pretty well. I gained 1 K/s in speed.. now its just + * robustness and printk's to be diked out. + * + * Revision 3.5 1995/01/18 22:29:59 guru + * still crashes the kernel when the lock_wait thing is woken up. + * + * Revision 3.4 1995/01/18 21:59:47 guru + * Broken set-bit locking snapshot + * + * Revision 3.3 1995/01/17 22:09:18 guru + * infinite sleep in a lock somewhere.. + * + * Revision 3.2 1995/01/15 16:46:06 guru + * Log trimmed of non-pertinant 1.x branch messages + * + * Revision 3.1 1995/01/15 14:41:45 guru + * New Scheduler and timer stuff... + * + * Revision 1.15 1995/01/15 14:29:02 guru + * Will make 1.14 (now 1.15) the 3.0 branch, and the 1.12 the 2.0 branch, the one + * with the dumber scheduler + * + * Revision 1.14 1995/01/15 02:37:08 guru + * shock.. the kept-new-versions could have zonked working + * stuff.. shudder + * + * Revision 1.13 1995/01/15 02:36:31 guru + * big changes + * + * scheduler was torn out and replaced with something smarter + * + * global names not prefixed with eql_ were renamed to protect + * against namespace collisions + * + * a few more abstract interfaces were added to facilitate any + * potential change of datastructure. the driver is still using + * a linked list of slaves. going to a heap would be a bit of + * an overkill. + * + * this compiles fine with no warnings. + * + * the locking mechanism and timer stuff must be written however, + * this version will not work otherwise + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "eql.h" + +#ifndef EQL_DEBUG +/* #undef EQL_DEBUG -* print nothing at all, not even a boot-banner */ +/* #define EQL_DEBUG 1 -* print only the boot-banner */ +/* #define EQL_DEBUG 5 -* print major function entries */ +/* #define EQL_DEBUG 20 -* print subfunction entries */ +/* #define EQL_DEBUG 50 -* print utility entries */ +/* #define EQL_DEBUG 100 -* print voluminous function entries */ +#define EQL_DEBUG 1 +#endif +static unsigned int eql_debug = EQL_DEBUG; + +int eql_init(struct device *dev); /* */ +static int eql_open(struct device *dev); /* */ +static int eql_close(struct device *dev); /* */ +static int eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd); /* */ +static int eql_slave_xmit(struct sk_buff *skb, struct device *dev); /* */ + +static struct enet_statistics *eql_get_stats(struct device *dev); /* */ +static int eql_header(unsigned char *buff, struct device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned len, struct sk_buff *skb); /* */ +static int eql_rebuild_header(void *buff, struct device *dev, + unsigned long raddr, struct sk_buff *skb); /* */ +static unsigned short eql_type_trans (struct sk_buff *skb, + struct device *dev); /* */ + +/* ioctl() handlers + ---------------- */ +static int eql_enslave(struct device *dev, slaving_request_t *srq); /* */ +static int eql_emancipate(struct device *dev, slaving_request_t *srq); /* */ + +static int eql_g_slave_cfg(struct device *dev, slave_config_t *sc); /* */ +static int eql_s_slave_cfg(struct device *dev, slave_config_t *sc); /* */ + +static int eql_g_master_cfg(struct device *dev, master_config_t *mc); /* */ +static int eql_s_master_cfg(struct device *dev, master_config_t *mc); /* */ + +static inline int eql_is_slave(struct device *dev); /* */ +static inline int eql_is_master(struct device *dev); /* */ + +static slave_t *eql_new_slave(void); /* */ +static void eql_delete_slave(slave_t *slave); /* */ + +/* static long eql_slave_priority(slave_t *slave); -* */ +static inline int eql_number_slaves(slave_queue_t *queue); /* */ + +static inline int eql_is_empty(slave_queue_t *queue); /* */ +static inline int eql_is_full(slave_queue_t *queue); /* */ + +static slave_queue_t *eql_new_slave_queue(struct device *dev); /* */ +static void eql_delete_slave_queue(slave_queue_t *queue); /* */ + +static int eql_insert_slave(slave_queue_t *queue, slave_t *slave); /* */ +static slave_t *eql_remove_slave(slave_queue_t *queue, slave_t *slave); /* */ + +/* static int eql_insert_slave_dev(slave_queue_t *queue, struct device *dev); -* */ +static int eql_remove_slave_dev(slave_queue_t *queue, struct device *dev); /* */ + +static inline struct device *eql_best_slave_dev(slave_queue_t *queue); /* */ +static inline slave_t *eql_best_slave(slave_queue_t *queue); /* */ +static inline slave_t *eql_first_slave(slave_queue_t *queue); /* */ +static inline slave_t *eql_next_slave(slave_queue_t *queue, slave_t *slave); /* */ + +static inline void eql_set_best_slave(slave_queue_t *queue, slave_t *slave); /* */ +static inline void eql_schedule_slaves(slave_queue_t *queue); /* */ + +static slave_t *eql_find_slave_dev(slave_queue_t *queue, struct device *dev); /* */ + +/* static inline eql_lock_slave_queue(slave_queue_t *queue); -* */ +/* static inline eql_unlock_slave_queue(slave_queue_t *queue); -* */ + +static void eql_timer(unsigned long param); /* */ + +/* struct device * interface functions + --------------------------------------------------------- + */ + +int +eql_init(struct device *dev) +{ + static unsigned version_printed = 0; + /* static unsigned num_masters = 0; */ + equalizer_t *eql = 0; + int i; + + if ( version_printed++ == 0 && eql_debug > 0) + printk(version); + + /* Initialize the device structure. */ + dev->priv = kmalloc (sizeof (equalizer_t), GFP_KERNEL); + memset (dev->priv, 0, sizeof (equalizer_t)); + eql = (equalizer_t *) dev->priv; + + eql->stats = kmalloc (sizeof (struct enet_statistics), GFP_KERNEL); + memset (eql->stats, 0, sizeof (struct enet_statistics)); + + init_timer (&eql->timer); + eql->timer.data = (unsigned long) dev->priv; + eql->timer.expires = EQL_DEFAULT_RESCHED_IVAL; + eql->timer.function = &eql_timer; + eql->timer_on = 0; + + dev->open = eql_open; + dev->stop = eql_close; + dev->do_ioctl = eql_ioctl; + dev->hard_start_xmit = eql_slave_xmit; + dev->get_stats = eql_get_stats; + + /* Fill in the fields of the device structure with ethernet-generic values. + This should be in a common file instead of per-driver. */ + + for (i = 0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); + + dev->hard_header = eql_header; + dev->rebuild_header = eql_rebuild_header; + dev->type_trans = eql_type_trans; + + /* now we undo some of the things that eth_setup does that we don't like */ + dev->mtu = EQL_DEFAULT_MTU; /* set to 576 in eql.h */ + dev->flags = IFF_MASTER; + + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = sizeof (unsigned long); + + dev->type = ARPHRD_SLIP; + + return 0; +} + + +static +int +eql_open(struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + slave_queue_t *new_queue; + +#ifdef EQL_DEBUG + if (eql_debug >= 5) + printk ("%s: open\n", dev->name); +#endif + + new_queue = eql_new_slave_queue (dev); + + if (new_queue != 0) + { + new_queue->master_dev = dev; + eql->queue = new_queue; + eql->queue->lock = 0; + eql->min_slaves = 1; + eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */ + + eql->timer_on = 1; + add_timer (&eql->timer); + + return 0; + } + return 1; +} + + +static +int +eql_close(struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + +#ifdef EQL_DEBUG + if ( eql_debug >= 5) + printk ("%s: close\n", dev->name); +#endif + /* The timer has to be stopped first before we start hacking away + at the data structure it scans every so often... */ + printk ("%s: stopping timer\n", dev->name); + eql->timer_on = 0; + del_timer (&eql->timer); + + eql_delete_slave_queue (eql->queue); + + return 0; +} + + +static +int +eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + switch (cmd) + { + case EQL_ENSLAVE: + return eql_enslave (dev, (slaving_request_t *) ifr->ifr_data); + case EQL_EMANCIPATE: + return eql_emancipate (dev, (slaving_request_t *) ifr->ifr_data); + + case EQL_GETSLAVECFG: + return eql_g_slave_cfg (dev, (slave_config_t *) ifr->ifr_data); + case EQL_SETSLAVECFG: + return eql_s_slave_cfg (dev, (slave_config_t *) ifr->ifr_data); + + case EQL_GETMASTRCFG: + return eql_g_master_cfg (dev, (master_config_t *) ifr->ifr_data); + case EQL_SETMASTRCFG: + return eql_s_master_cfg (dev, (master_config_t *) ifr->ifr_data); + + default: + return -EOPNOTSUPP; + } +} + + +static +int +eql_slave_xmit(struct sk_buff *skb, struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + struct device *slave_dev = 0; + slave_t *slave; + + if (skb == NULL) + { + return 0; + } + + eql_schedule_slaves (eql->queue); + + slave_dev = eql_best_slave_dev (eql->queue); + slave = eql_best_slave (eql->queue); + + if ( slave_dev != 0 ) + { +#ifdef EQL_DEBUG + if (eql_debug >= 100) + printk ("%s: %d slaves xmitng %ld B %s\n", + dev->name, eql_number_slaves (eql->queue), skb->len, + slave_dev->name); +#endif + + dev_queue_xmit (skb, slave_dev, 1); + eql->stats->tx_packets++; + slave->bytes_queued += skb->len; + } + else + { + /* The alternative for this is the return 1 and have + dev_queue_xmit just queue it up on the eql's queue. */ + + eql->stats->tx_dropped++; + dev_kfree_skb(skb, FREE_WRITE); + } + return 0; +} + + +static +struct enet_statistics * +eql_get_stats(struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + + return eql->stats; +} + + +static +int +eql_header(unsigned char *buff, struct device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned len, struct sk_buff *skb) +{ + return 0; +} + + +static +int +eql_rebuild_header(void *buff, struct device *dev, + unsigned long raddr, struct sk_buff *skb) +{ + return 0; +} + + +static +unsigned short +eql_type_trans (struct sk_buff *skb, struct device *dev) +{ + return htons (ETH_P_IP); +} + + +/* private ioctl functions + ----------------------------------------------------------------- + */ + +static int +eql_enslave(struct device *dev, slaving_request_t *srqp) +{ + struct device *master_dev; + struct device *slave_dev; + slaving_request_t srq; + + if (! suser ()) + return -EPERM; + + memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t)); + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: enslave '%s' %ld bps\n", dev->name, + srq.slave_name, srq.priority); +#endif + + master_dev = dev; /* for "clarity" */ + slave_dev = dev_get (srq.slave_name); + + if (master_dev != 0 && slave_dev != 0) + { + if (! eql_is_master (slave_dev) && /* slave is not a master */ + ! eql_is_slave (slave_dev) ) /* slave is not already a slave */ + { + slave_t *s = eql_new_slave (); + equalizer_t *eql = (equalizer_t *) master_dev->priv; + + s->dev = slave_dev; + s->priority = srq.priority; + s->priority_bps = srq.priority; + s->priority_Bps = srq.priority / 8; + + slave_dev->flags |= IFF_SLAVE; + + eql_insert_slave (eql->queue, s); + + return 0; + } + return -EINVAL; + } + return -EINVAL; +} + + + +static +int +eql_emancipate(struct device *dev, slaving_request_t *srqp) +{ + struct device *master_dev; + struct device *slave_dev; + slaving_request_t srq; + + if (! suser ()) + return -EPERM; + + memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t)); + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: emancipate `%s`\n", dev->name, srq.slave_name); +#endif + + + master_dev = dev; /* for "clarity" */ + slave_dev = dev_get (srq.slave_name); + + if ( eql_is_slave (slave_dev) ) /* really is a slave */ + { + equalizer_t *eql = (equalizer_t *) master_dev->priv; + slave_dev->flags = slave_dev->flags & ~IFF_SLAVE; + + eql_remove_slave_dev (eql->queue, slave_dev); + + return 0; + } + return -EINVAL; +} + + +static +int +eql_g_slave_cfg(struct device *dev, slave_config_t *scp) +{ + slave_t *slave; + equalizer_t *eql; + struct device *slave_dev; + slave_config_t sc; + + memcpy_fromfs (&sc, scp, sizeof (slave_config_t)); + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: get config for slave `%s'\n", dev->name, sc.slave_name); +#endif + + eql = (equalizer_t *) dev->priv; + slave_dev = dev_get (sc.slave_name); + + if ( eql_is_slave (slave_dev) ) + { + slave = eql_find_slave_dev (eql->queue, slave_dev); + if (slave != 0) + { + sc.priority = slave->priority; + memcpy_tofs (scp, &sc, sizeof (slave_config_t)); + return 0; + } + } + return -EINVAL; +} + + +static +int +eql_s_slave_cfg(struct device *dev, slave_config_t *scp) +{ + slave_t *slave; + equalizer_t *eql; + struct device *slave_dev; + slave_config_t sc; + + if (! suser ()) + return -EPERM; + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: set config for slave `%s'\n", dev->name, sc.slave_name); +#endif + + memcpy_fromfs (&sc, scp, sizeof (slave_config_t)); + + eql = (equalizer_t *) dev->priv; + slave_dev = dev_get (sc.slave_name); + + if ( eql_is_slave (slave_dev) ) + { + slave = eql_find_slave_dev (eql->queue, slave_dev); + if (slave != 0) + { + slave->priority = sc.priority; + slave->priority_bps = sc.priority; + slave->priority_Bps = sc.priority / 8; + return 0; + } + } + return -EINVAL; +} + + +static +int +eql_g_master_cfg(struct device *dev, master_config_t *mcp) +{ + equalizer_t *eql; + master_config_t mc; + +#if EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: get master config\n", dev->name); +#endif + + if ( eql_is_master (dev) ) + { + eql = (equalizer_t *) dev->priv; + mc.max_slaves = eql->max_slaves; + mc.min_slaves = eql->min_slaves; + memcpy_tofs (mcp, &mc, sizeof (master_config_t)); + return 0; + } + return -EINVAL; +} + + +static +int +eql_s_master_cfg(struct device *dev, master_config_t *mcp) +{ + equalizer_t *eql; + master_config_t mc; + + if (! suser ()) + return -EPERM; + +#if EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: set master config\n", dev->name); +#endif + + memcpy_fromfs (&mc, mcp, sizeof (master_config_t)); + + if ( eql_is_master (dev) ) + { + eql = (equalizer_t *) dev->priv; + eql->max_slaves = mc.max_slaves; + eql->min_slaves = mc.min_slaves; + return 0; + } + return -EINVAL; +} + +/* private device support functions + ------------------------------------------------------------------ + */ + +static inline +int +eql_is_slave(struct device *dev) +{ + if (dev) + { + if ((dev->flags & IFF_SLAVE) == IFF_SLAVE) + return 1; + } + return 0; +} + + +static inline +int +eql_is_master(struct device *dev) +{ + if (dev) + { + if ((dev->flags & IFF_MASTER) == IFF_MASTER) + return 1; + } + return 0; +} + + +static +slave_t * +eql_new_slave(void) +{ + slave_t *slave; + + slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL); + if (slave) + { + memset(slave, 0, sizeof (slave_t)); + return slave; + } + return 0; +} + + +static +void +eql_delete_slave(slave_t *slave) +{ + kfree (slave); +} + + +#if 0 /* not currently used, will be used + when we realy use a priority queue */ +static +long +slave_Bps(slave_t *slave) +{ + return (slave->priority_Bps); +} + +static +long +slave_bps(slave_t *slave) +{ + return (slave->priority_bps); +} +#endif + + +static inline +int +eql_number_slaves(slave_queue_t *queue) +{ + return queue->num_slaves; +} + + +static inline +int +eql_is_empty(slave_queue_t *queue) +{ + if (eql_number_slaves (queue) == 0) + return 1; + return 0; +} + + +static inline +int +eql_is_full(slave_queue_t *queue) +{ + equalizer_t *eql = (equalizer_t *) queue->master_dev->priv; + + if (eql_number_slaves (queue) == eql->max_slaves) + return 1; + return 0; +} + + +static +slave_queue_t * +eql_new_slave_queue(struct device *dev) +{ + slave_queue_t *queue; + slave_t *head_slave; + slave_t *tail_slave; + + queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL); + memset (queue, 0, sizeof (slave_queue_t)); + + head_slave = eql_new_slave (); + tail_slave = eql_new_slave (); + + if ( head_slave != 0 && + tail_slave != 0 ) + { + head_slave->next = tail_slave; + tail_slave->next = 0; + queue->head = head_slave; + queue->num_slaves = 0; + queue->master_dev = dev; + } + else + { + kfree (queue); + return 0; + } + return queue; +} + + +static +void +eql_delete_slave_queue(slave_queue_t *queue) +{ + slave_t *zapped; + + /* this should only be called when there isn't a timer running that scans + the data periodicaly.. dev_close stops the timer... */ + + while ( ! eql_is_empty (queue) ) + { + zapped = eql_remove_slave (queue, queue->head->next); + eql_delete_slave (zapped); + } + kfree (queue->head->next); + kfree (queue->head); + kfree (queue); +} + + +static +int +eql_insert_slave(slave_queue_t *queue, slave_t *slave) +{ + cli (); + + if ( ! eql_is_full (queue) ) + { + slave_t *duplicate_slave = 0; + + duplicate_slave = eql_find_slave_dev (queue, slave->dev); + + if (duplicate_slave != 0) + { +/* printk ("%s: found a duplicate, killing it and replacing\n", + queue->master_dev->name); */ + eql_delete_slave (eql_remove_slave (queue, duplicate_slave)); + } + + slave->next = queue->head->next; + queue->head->next = slave; + queue->num_slaves++; + sti (); + return 0; + } + + sti (); + + return 1; +} + + +static +slave_t * +eql_remove_slave(slave_queue_t *queue, slave_t *slave) +{ + slave_t *prev; + slave_t *current; + + cli (); + + prev = queue->head; + current = queue->head->next; + while (current != slave && + current->dev != 0 ) + { +/* printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */ + prev = current; + current = current->next; + } + + if (current == slave) + { + prev->next = current->next; + queue->num_slaves--; + + current->dev->flags = current->dev->flags & ~IFF_SLAVE; + + return current; + } + + sti (); + + return 0; /* not found */ +} + + +#if 0 +static +int +eql_insert_slave_dev(slave_queue_t *queue, struct device *dev) +{ + slave_t *slave; + + cli (); + + if ( ! eql_is_full (queue) ) + { + slave = eql_new_slave (); + slave->dev = dev; + slave->priority = EQL_DEFAULT_SLAVE_PRIORITY; + slave->priority_bps = EQL_DEFAULT_SLAVE_PRIORITY; + slave->priority_Bps = EQL_DEFAULT_SLAVE_PRIORITY / 8; + slave->next = queue->head->next; + queue->head->next = slave; + sti (); + return 0; + } + sti (); + return 1; +} +#endif + + +static +int +eql_remove_slave_dev(slave_queue_t *queue, struct device *dev) +{ + slave_t *prev; + slave_t *current; + slave_t *target; + + target = eql_find_slave_dev (queue, dev); + + if (target != 0) + { + cli (); + + prev = queue->head; + current = prev->next; + while (current != target) + { + prev = current; + current = current->next; + } + prev->next = current->next; + queue->num_slaves--; + + sti (); + + eql_delete_slave (current); + return 0; + } + return 1; +} + + +static inline +struct device * +eql_best_slave_dev(slave_queue_t *queue) +{ + if (queue->best_slave != 0) + { + if (queue->best_slave->dev != 0) + return queue->best_slave->dev; + else + return 0; + } + else + return 0; +} + + +static inline +slave_t * +eql_best_slave(slave_queue_t *queue) +{ + return queue->best_slave; +} + +static inline +void +eql_schedule_slaves(slave_queue_t *queue) +{ + struct device *master_dev = queue->master_dev; + slave_t *best_slave = 0; + slave_t *slave_corpse = 0; + +#ifdef EQL_DEBUG + if (eql_debug >= 100) + printk ("%s: schedule %d slaves\n", + master_dev->name, eql_number_slaves (queue)); +#endif + + if ( eql_is_empty (queue) ) + { + /* no slaves to play with */ + eql_set_best_slave (queue, (slave_t *) 0); + return; + } + else + { /* make a pass to set the best slave */ + unsigned long best_load = (unsigned long) ULONG_MAX; + slave_t *slave = 0; + int i; + + cli (); + + for (i = 1, slave = eql_first_slave (queue); + i <= eql_number_slaves (queue); + i++, slave = eql_next_slave (queue, slave)) + { + /* go through the slave list once, updating best_slave + whenever a new best_load is found, whenever a dead + slave is found, it is marked to be pulled out of the + queue */ + + unsigned long slave_load; + unsigned long bytes_queued; + unsigned long priority_Bps; + + if (slave != 0) + { + bytes_queued = slave->bytes_queued; + priority_Bps = slave->priority_Bps; + + if ( slave->dev != 0) + { + if ( slave->dev->flags & IFF_UP == IFF_UP ) + { + slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + (priority_Bps) + bytes_queued * 8; + + if (slave_load < best_load) + { + best_load = slave_load; + best_slave = slave; + } + } + else /* we found a dead slave */ + { + /* we only bury one slave at a time, if more than + one slave dies, we will bury him on the next + reschedule. slaves don't die all at once that much + anyway */ + slave_corpse = slave; + } + } + } + } /* for */ + + sti (); + + eql_set_best_slave (queue, best_slave); + } /* else */ + + if (slave_corpse != 0) + { + printk ("eql: scheduler found dead slave, burying...\n"); + eql_delete_slave (eql_remove_slave (queue, slave_corpse)); + } + + return; +} + + +static +slave_t * +eql_find_slave_dev(slave_queue_t *queue, struct device *dev) +{ + slave_t *slave = 0; + + slave = eql_first_slave(queue); + + while (slave != 0 && slave->dev != dev && slave != 0) + { +#if 0 + if (slave->dev != 0) + printk ("eql: find_slave_dev; looked at '%s'...\n", slave->dev->name); + else + printk ("eql: find_slave_dev; looked at nothing...\n"); +#endif + + slave = slave->next; + } + + return slave; +} + + +static inline +slave_t * +eql_first_slave(slave_queue_t *queue) +{ + return queue->head->next; +} + + +static inline +slave_t * +eql_next_slave(slave_queue_t *queue, slave_t *slave) +{ + return slave->next; +} + + +static inline +void +eql_set_best_slave(slave_queue_t *queue, slave_t *slave) +{ + queue->best_slave = slave; +} + + +#if 0 +static inline +int +eql_lock_slave_queue(slave_queue_t *queue) +{ + int result = 0; + + printk ("eql: lock == %d\n", queue->lock); + if (queue->lock) + { + printk ("eql: lock_slave-q sleeping for lock\n"); + sleep_on (&eql_queue_lock); + printk ("eql: lock_slave-q woken up\n"); + queue->lock = 1; + } + queue->lock = 1; + return result; +} + +static inline +int +eql_unlock_slave_queue(slave_queue_t *queue) +{ + int result = 0; + + if (queue->lock != 0) + { + queue->lock = 0; + printk ("eql: unlock_slave-q waking up lock waiters\n"); + wake_up (&eql_queue_lock); + } + return result; +} +#endif + +static inline +int +eql_is_locked_slave_queue(slave_queue_t *queue) +{ + return test_bit(1, (void *) &queue->lock); +} + +static +void +eql_timer(unsigned long param) +{ + equalizer_t *eql = (equalizer_t *) param; + slave_t *slave; + slave_t *slave_corpse = 0; + int i; + + if ( ! eql_is_empty (eql->queue) ) + { + cli (); + + for (i = 1, slave = eql_first_slave (eql->queue); + i <= eql_number_slaves (eql->queue); + i++, slave = eql_next_slave (eql->queue, slave)) + { + if (slave != 0) + { + if ( slave->dev->flags & IFF_UP == IFF_UP ) + { + slave->bytes_queued -= slave->priority_Bps; + + if (slave->bytes_queued < 0) + slave->bytes_queued = 0; + } + else + { + slave_corpse = slave; + } + } + } + + sti (); + + if (slave_corpse != 0) + { + printk ("eql: timer found dead slave, burying...\n"); + eql_delete_slave (eql_remove_slave (eql->queue, slave_corpse)); + } + + } + + if (eql->timer_on != 0) + { + eql->timer.expires = EQL_DEFAULT_RESCHED_IVAL; + add_timer (&eql->timer); + } +} + +/* + * Local Variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c eql.c" + * version-control: t + * kept-new-versions: 20 + * End: + */ diff -u --recursive --new-file linux-1.2.2/drivers/net/eql.c~ linux/drivers/net/eql.c~ --- linux-1.2.2/drivers/net/eql.c~ Wed Dec 31 19:00:00 1969 +++ linux/drivers/net/eql.c~ Fri Mar 31 23:05:50 1995 @@ -0,0 +1,1169 @@ +/* + * Equalizer Load-balancer for serial network interfaces. + * + * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes + * NCM: Network and Communications Mangement, Inc. + * + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * The author may be reached as simon@ncm.com, or C/O + * NCM + * Attn: Simon Janes + * 6803 Whittier Ave + * McLean VA 22101 + * Phone: 1-703-847-0040 ext 103 + */ + +static char *version = + "EQL Multilink Driver: $Revision: 1.2 $ $Date: 1995/03/25 18:16:32 $ Simon Janes (simon@ncm.com)\n"; + +#include + +/* + * Sources: + * skeleton.c by Donald Becker. + * Inspirations: + * The Harried and Overworked Alan Cox + * Conspiracies: + * The Alan Cox and Arisian plot to get someone else to do the code, which + * turned out to be me. + */ + +/* + * $Log: eql.c,v $ + * Revision 1.2 1995/03/25 18:16:32 guru + * Will have to make this the 4.0 branch + * + * Revision 1.1 1995/03/25 18:14:05 guru + * Initial revision + * + * Revision 3.11 1995/01/19 23:14:31 guru + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued * 8; + * + * Revision 3.10 1995/01/19 23:07:53 guru + * back to + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued; + * + * Revision 3.9 1995/01/19 22:38:20 guru + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued * 4; + * + * Revision 3.8 1995/01/19 22:30:55 guru + * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + * (priority_Bps) + bytes_queued * 2; + * + * Revision 3.7 1995/01/19 21:52:35 guru + * printk's trimmed out. + * + * Revision 3.6 1995/01/19 21:49:56 guru + * This is working pretty well. I gained 1 K/s in speed.. now its just + * robustness and printk's to be diked out. + * + * Revision 3.5 1995/01/18 22:29:59 guru + * still crashes the kernel when the lock_wait thing is woken up. + * + * Revision 3.4 1995/01/18 21:59:47 guru + * Broken set-bit locking snapshot + * + * Revision 3.3 1995/01/17 22:09:18 guru + * infinite sleep in a lock somewhere.. + * + * Revision 3.2 1995/01/15 16:46:06 guru + * Log trimmed of non-pertinant 1.x branch messages + * + * Revision 3.1 1995/01/15 14:41:45 guru + * New Scheduler and timer stuff... + * + * Revision 1.15 1995/01/15 14:29:02 guru + * Will make 1.14 (now 1.15) the 3.0 branch, and the 1.12 the 2.0 branch, the one + * with the dumber scheduler + * + * Revision 1.14 1995/01/15 02:37:08 guru + * shock.. the kept-new-versions could have zonked working + * stuff.. shudder + * + * Revision 1.13 1995/01/15 02:36:31 guru + * big changes + * + * scheduler was torn out and replaced with something smarter + * + * global names not prefixed with eql_ were renamed to protect + * against namespace collisions + * + * a few more abstract interfaces were added to facilitate any + * potential change of datastructure. the driver is still using + * a linked list of slaves. going to a heap would be a bit of + * an overkill. + * + * this compiles fine with no warnings. + * + * the locking mechanism and timer stuff must be written however, + * this version will not work otherwise + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "eql.h" + +#ifndef EQL_DEBUG +/* #undef EQL_DEBUG -* print nothing at all, not even a boot-banner */ +/* #define EQL_DEBUG 1 -* print only the boot-banner */ +/* #define EQL_DEBUG 5 -* print major function entries */ +/* #define EQL_DEBUG 20 -* print subfunction entries */ +/* #define EQL_DEBUG 50 -* print utility entries */ +/* #define EQL_DEBUG 100 -* print voluminous function entries */ +#define EQL_DEBUG 1 +#endif +static unsigned int eql_debug = EQL_DEBUG; + +int eql_init(struct device *dev); /* */ +static int eql_open(struct device *dev); /* */ +static int eql_close(struct device *dev); /* */ +static int eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd); /* */ +static int eql_slave_xmit(struct sk_buff *skb, struct device *dev); /* */ + +static struct enet_statistics *eql_get_stats(struct device *dev); /* */ +static int eql_header(unsigned char *buff, struct device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned len, struct sk_buff *skb); /* */ +static int eql_rebuild_header(void *buff, struct device *dev, + unsigned long raddr, struct sk_buff *skb); /* */ +static unsigned short eql_type_trans (struct sk_buff *skb, + struct device *dev); /* */ + +/* ioctl() handlers + ---------------- */ +static int eql_enslave(struct device *dev, slaving_request_t *srq); /* */ +static int eql_emancipate(struct device *dev, slaving_request_t *srq); /* */ + +static int eql_g_slave_cfg(struct device *dev, slave_config_t *sc); /* */ +static int eql_s_slave_cfg(struct device *dev, slave_config_t *sc); /* */ + +static int eql_g_master_cfg(struct device *dev, master_config_t *mc); /* */ +static int eql_s_master_cfg(struct device *dev, master_config_t *mc); /* */ + +static inline int eql_is_slave(struct device *dev); /* */ +static inline int eql_is_master(struct device *dev); /* */ + +static slave_t *eql_new_slave(void); /* */ +static void eql_delete_slave(slave_t *slave); /* */ + +/* static long eql_slave_priority(slave_t *slave); -* */ +static inline int eql_number_slaves(slave_queue_t *queue); /* */ + +static inline int eql_is_empty(slave_queue_t *queue); /* */ +static inline int eql_is_full(slave_queue_t *queue); /* */ + +static slave_queue_t *eql_new_slave_queue(struct device *dev); /* */ +static void eql_delete_slave_queue(slave_queue_t *queue); /* */ + +static int eql_insert_slave(slave_queue_t *queue, slave_t *slave); /* */ +static slave_t *eql_remove_slave(slave_queue_t *queue, slave_t *slave); /* */ + +/* static int eql_insert_slave_dev(slave_queue_t *queue, struct device *dev); -* */ +static int eql_remove_slave_dev(slave_queue_t *queue, struct device *dev); /* */ + +static inline struct device *eql_best_slave_dev(slave_queue_t *queue); /* */ +static inline slave_t *eql_best_slave(slave_queue_t *queue); /* */ +static inline slave_t *eql_first_slave(slave_queue_t *queue); /* */ +static inline slave_t *eql_next_slave(slave_queue_t *queue, slave_t *slave); /* */ + +static inline void eql_set_best_slave(slave_queue_t *queue, slave_t *slave); /* */ +static inline void eql_schedule_slaves(slave_queue_t *queue); /* */ + +static slave_t *eql_find_slave_dev(slave_queue_t *queue, struct device *dev); /* */ + +/* static inline eql_lock_slave_queue(slave_queue_t *queue); -* */ +/* static inline eql_unlock_slave_queue(slave_queue_t *queue); -* */ + +static void eql_timer(unsigned long param); /* */ + +/* struct device * interface functions + --------------------------------------------------------- + */ + +int +eql_init(struct device *dev) +{ + static unsigned version_printed = 0; + /* static unsigned num_masters = 0; */ + equalizer_t *eql = 0; + int i; + + if ( version_printed++ == 0 && eql_debug > 0) + printk(version); + + /* Initialize the device structure. */ + dev->priv = kmalloc (sizeof (equalizer_t), GFP_KERNEL); + memset (dev->priv, 0, sizeof (equalizer_t)); + eql = (equalizer_t *) dev->priv; + + eql->stats = kmalloc (sizeof (struct enet_statistics), GFP_KERNEL); + memset (eql->stats, 0, sizeof (struct enet_statistics)); + + init_timer (&eql->timer); + eql->timer.data = (unsigned long) dev->priv; + eql->timer.expires = EQL_DEFAULT_RESCHED_IVAL; + eql->timer.function = &eql_timer; + eql->timer_on = 0; + + dev->open = eql_open; + dev->stop = eql_close; + dev->do_ioctl = eql_ioctl; + dev->hard_start_xmit = eql_slave_xmit; + dev->get_stats = eql_get_stats; + + /* Fill in the fields of the device structure with ethernet-generic values. + This should be in a common file instead of per-driver. */ + + for (i = 0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); + + dev->hard_header = eql_header; + dev->rebuild_header = eql_rebuild_header; + dev->type_trans = eql_type_trans; + + /* now we undo some of the things that eth_setup does that we don't like */ + dev->mtu = EQL_DEFAULT_MTU; /* set to 576 in eql.h */ + dev->flags = IFF_MASTER; + + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = sizeof (unsigned long); + + dev->type = ARPHRD_SLIP; + + return 0; +} + + +static +int +eql_open(struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + slave_queue_t *new_queue; + +#ifdef EQL_DEBUG + if (eql_debug >= 5) + printk ("%s: open\n", dev->name); +#endif + + new_queue = eql_new_slave_queue (dev); + + if (new_queue != 0) + { + new_queue->master_dev = dev; + eql->queue = new_queue; + eql->queue->lock = 0; + eql->min_slaves = 1; + eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */ + + printk ("%s: adding timer\n", dev->name); + eql->timer_on = 1; + add_timer (&eql->timer); + + return 0; + } + return 1; +} + + +static +int +eql_close(struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + +#ifdef EQL_DEBUG + if ( eql_debug >= 5) + printk ("%s: close\n", dev->name); +#endif + /* The timer has to be stopped first before we start hacking away + at the data structure it scans every so often... */ + printk ("%s: stopping timer\n", dev->name); + eql->timer_on = 0; + del_timer (&eql->timer); + + eql_delete_slave_queue (eql->queue); + + return 0; +} + + +static +int +eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + switch (cmd) + { + case EQL_ENSLAVE: + return eql_enslave (dev, (slaving_request_t *) ifr->ifr_data); + case EQL_EMANCIPATE: + return eql_emancipate (dev, (slaving_request_t *) ifr->ifr_data); + + case EQL_GETSLAVECFG: + return eql_g_slave_cfg (dev, (slave_config_t *) ifr->ifr_data); + case EQL_SETSLAVECFG: + return eql_s_slave_cfg (dev, (slave_config_t *) ifr->ifr_data); + + case EQL_GETMASTRCFG: + return eql_g_master_cfg (dev, (master_config_t *) ifr->ifr_data); + case EQL_SETMASTRCFG: + return eql_s_master_cfg (dev, (master_config_t *) ifr->ifr_data); + + default: + return -EOPNOTSUPP; + } +} + + +static +int +eql_slave_xmit(struct sk_buff *skb, struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + struct device *slave_dev = 0; + slave_t *slave; + + if (skb == NULL) + { + return 0; + } + + eql_schedule_slaves (eql->queue); + + slave_dev = eql_best_slave_dev (eql->queue); + slave = eql_best_slave (eql->queue); + + if ( slave_dev != 0 ) + { +#ifdef EQL_DEBUG + if (eql_debug >= 100) + printk ("%s: %d slaves xmitng %ld B %s\n", + dev->name, eql_number_slaves (eql->queue), skb->len, + slave_dev->name); +#endif + + dev_queue_xmit (skb, slave_dev, 1); + eql->stats->tx_packets++; + slave->bytes_queued += skb->len; + } + else + { + /* The alternative for this is the return 1 and have + dev_queue_xmit just queue it up on the eql's queue. */ + + eql->stats->tx_dropped++; + dev_kfree_skb(skb, FREE_WRITE); + } + return 0; +} + + +static +struct enet_statistics * +eql_get_stats(struct device *dev) +{ + equalizer_t *eql = (equalizer_t *) dev->priv; + + return eql->stats; +} + + +static +int +eql_header(unsigned char *buff, struct device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned len, struct sk_buff *skb) +{ + return 0; +} + + +static +int +eql_rebuild_header(void *buff, struct device *dev, + unsigned long raddr, struct sk_buff *skb) +{ + return 0; +} + + +static +unsigned short +eql_type_trans (struct sk_buff *skb, struct device *dev) +{ + return htons (ETH_P_IP); +} + + +/* private ioctl functions + ----------------------------------------------------------------- + */ + +static int +eql_enslave(struct device *dev, slaving_request_t *srqp) +{ + struct device *master_dev; + struct device *slave_dev; + slaving_request_t srq; + + if (! suser ()) + return -EPERM; + + memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t)); + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: enslave '%s' %ld bps\n", dev->name, + srq.slave_name, srq.priority); +#endif + + master_dev = dev; /* for "clarity" */ + slave_dev = dev_get (srq.slave_name); + + if (master_dev != 0 && slave_dev != 0) + { + if (! eql_is_master (slave_dev) && /* slave is not a master */ + ! eql_is_slave (slave_dev) ) /* slave is not already a slave */ + { + slave_t *s = eql_new_slave (); + equalizer_t *eql = (equalizer_t *) master_dev->priv; + + s->dev = slave_dev; + s->priority = srq.priority; + s->priority_bps = srq.priority; + s->priority_Bps = srq.priority / 8; + + slave_dev->flags |= IFF_SLAVE; + + eql_insert_slave (eql->queue, s); + + return 0; + } + return -EINVAL; + } + return -EINVAL; +} + + + +static +int +eql_emancipate(struct device *dev, slaving_request_t *srqp) +{ + struct device *master_dev; + struct device *slave_dev; + slaving_request_t srq; + + if (! suser ()) + return -EPERM; + + memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t)); + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: emancipate `%s`\n", dev->name, srq.slave_name); +#endif + + + master_dev = dev; /* for "clarity" */ + slave_dev = dev_get (srq.slave_name); + + if ( eql_is_slave (slave_dev) ) /* really is a slave */ + { + equalizer_t *eql = (equalizer_t *) master_dev->priv; + slave_dev->flags = slave_dev->flags & ~IFF_SLAVE; + + eql_remove_slave_dev (eql->queue, slave_dev); + + return 0; + } + return -EINVAL; +} + + +static +int +eql_g_slave_cfg(struct device *dev, slave_config_t *scp) +{ + slave_t *slave; + equalizer_t *eql; + struct device *slave_dev; + slave_config_t sc; + + memcpy_fromfs (&sc, scp, sizeof (slave_config_t)); + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: get config for slave `%s'\n", dev->name, sc.slave_name); +#endif + + eql = (equalizer_t *) dev->priv; + slave_dev = dev_get (sc.slave_name); + + if ( eql_is_slave (slave_dev) ) + { + slave = eql_find_slave_dev (eql->queue, slave_dev); + if (slave != 0) + { + sc.priority = slave->priority; + memcpy_tofs (scp, &sc, sizeof (slave_config_t)); + return 0; + } + } + return -EINVAL; +} + + +static +int +eql_s_slave_cfg(struct device *dev, slave_config_t *scp) +{ + slave_t *slave; + equalizer_t *eql; + struct device *slave_dev; + slave_config_t sc; + + if (! suser ()) + return -EPERM; + +#ifdef EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: set config for slave `%s'\n", dev->name, sc.slave_name); +#endif + + memcpy_fromfs (&sc, scp, sizeof (slave_config_t)); + + eql = (equalizer_t *) dev->priv; + slave_dev = dev_get (sc.slave_name); + + if ( eql_is_slave (slave_dev) ) + { + slave = eql_find_slave_dev (eql->queue, slave_dev); + if (slave != 0) + { + slave->priority = sc.priority; + slave->priority_bps = sc.priority; + slave->priority_Bps = sc.priority / 8; + return 0; + } + } + return -EINVAL; +} + + +static +int +eql_g_master_cfg(struct device *dev, master_config_t *mcp) +{ + equalizer_t *eql; + master_config_t mc; + +#if EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: get master config\n", dev->name); +#endif + + if ( eql_is_master (dev) ) + { + eql = (equalizer_t *) dev->priv; + mc.max_slaves = eql->max_slaves; + mc.min_slaves = eql->min_slaves; + memcpy_tofs (mcp, &mc, sizeof (master_config_t)); + return 0; + } + return -EINVAL; +} + + +static +int +eql_s_master_cfg(struct device *dev, master_config_t *mcp) +{ + equalizer_t *eql; + master_config_t mc; + + if (! suser ()) + return -EPERM; + +#if EQL_DEBUG + if (eql_debug >= 20) + printk ("%s: set master config\n", dev->name); +#endif + + memcpy_fromfs (&mc, mcp, sizeof (master_config_t)); + + if ( eql_is_master (dev) ) + { + eql = (equalizer_t *) dev->priv; + eql->max_slaves = mc.max_slaves; + eql->min_slaves = mc.min_slaves; + return 0; + } + return -EINVAL; +} + +/* private device support functions + ------------------------------------------------------------------ + */ + +static inline +int +eql_is_slave(struct device *dev) +{ + if (dev) + { + if ((dev->flags & IFF_SLAVE) == IFF_SLAVE) + return 1; + } + return 0; +} + + +static inline +int +eql_is_master(struct device *dev) +{ + if (dev) + { + if ((dev->flags & IFF_MASTER) == IFF_MASTER) + return 1; + } + return 0; +} + + +static +slave_t * +eql_new_slave(void) +{ + slave_t *slave; + + slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL); + if (slave) + { + memset(slave, 0, sizeof (slave_t)); + return slave; + } + return 0; +} + + +static +void +eql_delete_slave(slave_t *slave) +{ + kfree (slave); +} + + +#if 0 /* not currently used, will be used + when we realy use a priority queue */ +static +long +slave_Bps(slave_t *slave) +{ + return (slave->priority_Bps); +} + +static +long +slave_bps(slave_t *slave) +{ + return (slave->priority_bps); +} +#endif + + +static inline +int +eql_number_slaves(slave_queue_t *queue) +{ + return queue->num_slaves; +} + + +static inline +int +eql_is_empty(slave_queue_t *queue) +{ + if (eql_number_slaves (queue) == 0) + return 1; + return 0; +} + + +static inline +int +eql_is_full(slave_queue_t *queue) +{ + equalizer_t *eql = (equalizer_t *) queue->master_dev->priv; + + if (eql_number_slaves (queue) == eql->max_slaves) + return 1; + return 0; +} + + +static +slave_queue_t * +eql_new_slave_queue(struct device *dev) +{ + slave_queue_t *queue; + slave_t *head_slave; + slave_t *tail_slave; + + queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL); + memset (queue, 0, sizeof (slave_queue_t)); + + head_slave = eql_new_slave (); + tail_slave = eql_new_slave (); + + if ( head_slave != 0 && + tail_slave != 0 ) + { + head_slave->next = tail_slave; + tail_slave->next = 0; + queue->head = head_slave; + queue->num_slaves = 0; + queue->master_dev = dev; + } + else + { + kfree (queue); + return 0; + } + return queue; +} + + +static +void +eql_delete_slave_queue(slave_queue_t *queue) +{ + slave_t *zapped; + + /* this should only be called when there isn't a timer running that scans + the data periodicaly.. dev_close stops the timer... */ + + while ( ! eql_is_empty (queue) ) + { + zapped = eql_remove_slave (queue, queue->head->next); + eql_delete_slave (zapped); + } + kfree (queue->head->next); + kfree (queue->head); + kfree (queue); +} + + +static +int +eql_insert_slave(slave_queue_t *queue, slave_t *slave) +{ + cli (); + + if ( ! eql_is_full (queue) ) + { + slave_t *duplicate_slave = 0; + + duplicate_slave = eql_find_slave_dev (queue, slave->dev); + + if (duplicate_slave != 0) + { +/* printk ("%s: found a duplicate, killing it and replacing\n", + queue->master_dev->name); */ + eql_delete_slave (eql_remove_slave (queue, duplicate_slave)); + } + + slave->next = queue->head->next; + queue->head->next = slave; + queue->num_slaves++; + sti (); + return 0; + } + + sti (); + + return 1; +} + + +static +slave_t * +eql_remove_slave(slave_queue_t *queue, slave_t *slave) +{ + slave_t *prev; + slave_t *current; + + cli (); + + prev = queue->head; + current = queue->head->next; + while (current != slave && + current->dev != 0 ) + { +/* printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */ + prev = current; + current = current->next; + } + + if (current == slave) + { + prev->next = current->next; + queue->num_slaves--; + + current->dev->flags = current->dev->flags & ~IFF_SLAVE; + + return current; + } + + sti (); + + return 0; /* not found */ +} + + +#if 0 +static +int +eql_insert_slave_dev(slave_queue_t *queue, struct device *dev) +{ + slave_t *slave; + + cli (); + + if ( ! eql_is_full (queue) ) + { + slave = eql_new_slave (); + slave->dev = dev; + slave->priority = EQL_DEFAULT_SLAVE_PRIORITY; + slave->priority_bps = EQL_DEFAULT_SLAVE_PRIORITY; + slave->priority_Bps = EQL_DEFAULT_SLAVE_PRIORITY / 8; + slave->next = queue->head->next; + queue->head->next = slave; + sti (); + return 0; + } + sti (); + return 1; +} +#endif + + +static +int +eql_remove_slave_dev(slave_queue_t *queue, struct device *dev) +{ + slave_t *prev; + slave_t *current; + slave_t *target; + + target = eql_find_slave_dev (queue, dev); + + if (target != 0) + { + cli (); + + prev = queue->head; + current = prev->next; + while (current != target) + { + prev = current; + current = current->next; + } + prev->next = current->next; + queue->num_slaves--; + + sti (); + + eql_delete_slave (current); + return 0; + } + return 1; +} + + +static inline +struct device * +eql_best_slave_dev(slave_queue_t *queue) +{ + if (queue->best_slave != 0) + { + if (queue->best_slave->dev != 0) + return queue->best_slave->dev; + else + return 0; + } + else + return 0; +} + + +static inline +slave_t * +eql_best_slave(slave_queue_t *queue) +{ + return queue->best_slave; +} + +static inline +void +eql_schedule_slaves(slave_queue_t *queue) +{ + struct device *master_dev = queue->master_dev; + slave_t *best_slave = 0; + slave_t *slave_corpse = 0; + +#ifdef EQL_DEBUG + if (eql_debug >= 100) + printk ("%s: schedule %d slaves\n", + master_dev->name, eql_number_slaves (queue)); +#endif + + if ( eql_is_empty (queue) ) + { + /* no slaves to play with */ + eql_set_best_slave (queue, (slave_t *) 0); + return; + } + else + { /* make a pass to set the best slave */ + unsigned long best_load = (unsigned long) ULONG_MAX; + slave_t *slave = 0; + int i; + + cli (); + + for (i = 1, slave = eql_first_slave (queue); + i <= eql_number_slaves (queue); + i++, slave = eql_next_slave (queue, slave)) + { + /* go through the slave list once, updating best_slave + whenever a new best_load is found, whenever a dead + slave is found, it is marked to be pulled out of the + queue */ + + unsigned long slave_load; + unsigned long bytes_queued; + unsigned long priority_Bps; + + if (slave != 0) + { + bytes_queued = slave->bytes_queued; + priority_Bps = slave->priority_Bps; + + if ( slave->dev != 0) + { + if ( slave->dev->flags & IFF_UP == IFF_UP ) + { + slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - + (priority_Bps) + bytes_queued * 8; + + if (slave_load < best_load) + { + best_load = slave_load; + best_slave = slave; + } + } + else /* we found a dead slave */ + { + /* we only bury one slave at a time, if more than + one slave dies, we will bury him on the next + reschedule. slaves don't die all at once that much + anyway */ + slave_corpse = slave; + } + } + } + } /* for */ + + sti (); + + eql_set_best_slave (queue, best_slave); + } /* else */ + + if (slave_corpse != 0) + { + printk ("eql: scheduler found dead slave, burying...\n"); + eql_delete_slave (eql_remove_slave (queue, slave_corpse)); + } + + return; +} + + +static +slave_t * +eql_find_slave_dev(slave_queue_t *queue, struct device *dev) +{ + slave_t *slave = 0; + + slave = eql_first_slave(queue); + + while (slave != 0 && slave->dev != dev && slave != 0) + { +#if 0 + if (slave->dev != 0) + printk ("eql: find_slave_dev; looked at '%s'...\n", slave->dev->name); + else + printk ("eql: find_slave_dev; looked at nothing...\n"); +#endif + + slave = slave->next; + } + + return slave; +} + + +static inline +slave_t * +eql_first_slave(slave_queue_t *queue) +{ + return queue->head->next; +} + + +static inline +slave_t * +eql_next_slave(slave_queue_t *queue, slave_t *slave) +{ + return slave->next; +} + + +static inline +void +eql_set_best_slave(slave_queue_t *queue, slave_t *slave) +{ + queue->best_slave = slave; +} + + +#if 0 +static inline +int +eql_lock_slave_queue(slave_queue_t *queue) +{ + int result = 0; + + printk ("eql: lock == %d\n", queue->lock); + if (queue->lock) + { + printk ("eql: lock_slave-q sleeping for lock\n"); + sleep_on (&eql_queue_lock); + printk ("eql: lock_slave-q woken up\n"); + queue->lock = 1; + } + queue->lock = 1; + return result; +} + +static inline +int +eql_unlock_slave_queue(slave_queue_t *queue) +{ + int result = 0; + + if (queue->lock != 0) + { + queue->lock = 0; + printk ("eql: unlock_slave-q waking up lock waiters\n"); + wake_up (&eql_queue_lock); + } + return result; +} +#endif + +static inline +int +eql_is_locked_slave_queue(slave_queue_t *queue) +{ + return test_bit(1, (void *) &queue->lock); +} + +static +void +eql_timer(unsigned long param) +{ + equalizer_t *eql = (equalizer_t *) param; + slave_t *slave; + slave_t *slave_corpse = 0; + int i; + + if ( ! eql_is_empty (eql->queue) ) + { + cli (); + + for (i = 1, slave = eql_first_slave (eql->queue); + i <= eql_number_slaves (eql->queue); + i++, slave = eql_next_slave (eql->queue, slave)) + { + if (slave != 0) + { + if ( slave->dev->flags & IFF_UP == IFF_UP ) + { + slave->bytes_queued -= slave->priority_Bps; + + if (slave->bytes_queued < 0) + slave->bytes_queued = 0; + } + else + { + slave_corpse = slave; + } + } + } + + sti (); + + if (slave_corpse != 0) + { + printk ("eql: timer found dead slave, burying...\n"); + eql_delete_slave (eql_remove_slave (eql->queue, slave_corpse)); + } + + } + + if (eql->timer_on != 0) + { + eql->timer.expires = EQL_DEFAULT_RESCHED_IVAL; + add_timer (&eql->timer); + } +} + +/* + * Local Variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c eql.c" + * version-control: t + * kept-new-versions: 20 + * End: + */ diff -u --recursive --new-file linux-1.2.2/drivers/net/eql.h linux/drivers/net/eql.h --- linux-1.2.2/drivers/net/eql.h Wed Dec 31 19:00:00 1969 +++ linux/drivers/net/eql.h Fri Mar 31 23:05:10 1995 @@ -0,0 +1,77 @@ +/* + * Equalizer Load-balancer for serial network interfaces. + * + * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes + * NCM: Network and Communications Mangement, Inc. + * + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * The author may be reached as simon@ncm.com, or C/O + * NCM + * Attn: Simon Janes + * 6803 Whittier Ave + * McLean VA 22101 + * Phone: 1-703-847-0040 ext 103 + */ + +#define EQL_DEFAULT_SLAVE_PRIORITY 28800 +#define EQL_DEFAULT_MAX_SLAVES 4 +#define EQL_DEFAULT_MTU 576 +#define EQL_DEFAULT_RESCHED_IVAL 100 + +#define EQL_ENSLAVE (SIOCDEVPRIVATE) +#define EQL_EMANCIPATE (SIOCDEVPRIVATE + 1) + +#define EQL_GETSLAVECFG (SIOCDEVPRIVATE + 2) +#define EQL_SETSLAVECFG (SIOCDEVPRIVATE + 3) + +#define EQL_GETMASTRCFG (SIOCDEVPRIVATE + 4) +#define EQL_SETMASTRCFG (SIOCDEVPRIVATE + 5) + +typedef struct slave { + struct device *dev; + long priority; + long priority_bps; + long priority_Bps; + long bytes_queued; + struct slave *next; +} slave_t; + +typedef struct slave_queue { + slave_t *head; + slave_t *best_slave; + int num_slaves; + struct device *master_dev; + char lock; +} slave_queue_t; + +typedef struct equalizer { + slave_queue_t *queue; + int min_slaves; + int max_slaves; + struct enet_statistics *stats; + struct timer_list timer; + char timer_on; +} equalizer_t; + +typedef struct master_config { + char master_name[16]; + int max_slaves; + int min_slaves; +} master_config_t; + +typedef struct slave_config { + char slave_name[16]; + long priority; +} slave_config_t; + +typedef struct slaving_request { + char slave_name[16]; + long priority; +} slaving_request_t; + + + + eql-1.2.ds1/eql-driver.txt0000644000175000017500000005007405737354371014536 0ustar roverrover EQL Driver: Serial IP Load Balancing HOWTO Simon "Guru Aleph-Null" Janes, simon@ncm.com v1.2, April 1, 1995 This is the manual for the EQL device driver. EQL is a software device that lets you load-balance IP serial links (SLIP or uncompressed PPP) to increase your bandwidth. It will not reduce your latency (i.e. ping times) except in the case where you already have lots of traffic on your link, in which it will help it out. This driver has been tested with the 1.2.1 kernel and should patch cleanly in future 1.2.x ker- nels. This driver is expected to be merged into the 1.3.x kernel very shortly. The eql-1.2.patch file was generated against the v1.2.2 ker- nel. 1. Introduction Which is worse? A huge fee for a 56K leased line or two phone lines? Its probably the former. If you find yourself craving more bandwidth, and have a ISP that is flexible, it is now possible to bind modems together to work as one point-to-point link to increase your bandwidth. All this without any need of special black box routers. The eql driver has been tested with the Livingston PortMaster-2e terminal server and with another Linux box running the eql driver in the reverse direction. Other terminal servers and routers are expected to be "proven" with the eql driver very shortly. ISPs should be more than happy to just charge you for the cost of using a second port, line and modem for your load balancing connections. If they are not, find a more flexible and open minded provider. 2. Kernel Configuration Here I describe the general steps of getting a kernel up and working with the eql driver. From patching, building, to installing. 2.1. Patching The Kernel 2.1.1. Obtaining the Patches As of this writing, the eql driver is not yet available in a kernel source tree. The driver works fine with only the eql_enslave program, although there are hooks for a eql_emancipate program and some other configuration programs. I do not know at this time if they will be implemented. If not, they will be cut out of the driver to conserve space. This documentation, driver, sample configs, and enslaving utility can be FTP'ed from: ftp://slaughter.ncm.com/pub/Linux/LOAD_BALANCING/eql-1.2.tar.gz Unpack this archive someplace obvious like /usr/local/lib/. It will create the following files (more or less): ______________________________________________________________________ -rw-r--r-- guru/ncm 198 Mar 31 23:35 1995 eql-1.2/NO-WARRANTY -rw-r--r-- guru/ncm 58226 Apr 1 04:15 1995 eql-1.2/eql-1.2.patch -rw-r--r-- guru/ncm 20540 Apr 1 17:48 1995 eql-1.2/eql-driver.txt -rwxr-xr-x guru/ncm 16111 Mar 31 23:35 1995 eql-1.2/eql_enslave -rw-r--r-- guru/ncm 2195 Mar 31 23:35 1995 eql-1.2/eql_enslave.c ______________________________________________________________________ Unpack a fresh kernel where you usually work on your kernels. You may want to move your "working" kernel sources out of the way in case you need to revert to a good source tree for other reasons. Apply the patch by running the commands: ______________________________________________________________________ cd /usr/src patch -p0 /dev/null if you don't want to get any error messages if the scripts have any problems. ______________________________________________________________________ # Run the ppp-eql-x scripts every minute of every day, etc... * * * * * /etc/ppp/ppp-eql-1 * * * * * /etc/ppp/ppp-eql-2 ______________________________________________________________________ o The PPP-eql Script You will want to change the DEVICE for each line naturally, the phone number if you aren't dialing into a rotary setup on the remote side. Note the >/dev/tty8 redirection. That is a status display which will show errors from the scripts and the dialogue between the modem and expect script. You will want to change this for each line you have. ______________________________________________________________________ #!/bin/sh PATH=$PATH:/usr/etc LOCKFILE_DIR=/var/spool/locks DEVICE=cua1 LOGIN_NAME=Pname PASSWORD=password PHONE_NO=555-1212 LOCAL_IP=199.199.199.1 cd /etc/ppp if [ -f /var/spool/lock/LCK..$DEVICE ] then # N.B. This will not work if you have a stale # lock file in the lock dir. exit 0 fi DIALER_CMD="/etc/ppp/dialout/Zoom-V.34.xp $PHONE_NO \ $LOGIN_NAME $PASSWORD $DEVICE" setserial /dev/$DEVICE spd_vhi stty `cat $DEVICE.stty` /dev/tty8 ______________________________________________________________________ o eql-options The -vj is probably the most important option. You must always keep in mind that Van Jacobson compression depends on packets coming in a serial order, which just does not happen when you have more than one path between two points. ______________________________________________________________________ modem crtscts lcp-echo-interval 10 lcp-echo-failure 6 -vj ______________________________________________________________________ o Zoom-V.34.xp I like to use Expect for modem dial scripts. This one prints status messages to stderr so I can redirect them and have a handly monitoring screen to watch the modem dial. If everyone would write up nifty little Expect scripts, it would be a good thing. The UUCP style monsters just aren't flexible enough. ______________________________________________________________________ #!/usr/local/bin/expect -f # Zoom-V.34 Modem Dialer set DialDesc "Zoom V.34 Modem Dialer" puts -nonewline stderr "\0330;0r\033\[46;30m\033\[H\033\[J" puts -nonewline stderr "$DialDesc: " if { $argc != 4 } { puts stderr "usage: DIALER.xp " exit } set PhoneNumber [lindex $argv 0] set LoginName [lindex $argv 1] set Password [lindex $argv 2] set DeviceName [lindex $argv 3] puts stderr "Dialing $PhoneNumber on $DeviceName. Login $LoginName." puts stderr "\033\[2;2H\033\[2;25r\033\[44;33;1m\033\[1;1d" for {set i 0} {$i < 56} {incr i 1} { puts stderr "~" } set InitString "AT&C1&D2W1L1S0=0%E2" proc print {string} { set CurrentDate [exec /bin/date "+%a %d %r"] puts stderr "$CurrentDate - $string" } set send_human { .05 .05 .05 .05 .05 } proc hayes_escape {} { print "+++" send "\r+++" set timeout 10 expect { "OK\r\n" {} timeout {} } } print "+++/ATH -- hanging up modem" hayes_escape print "AT -- sending modem attention" send -h "AT\r\n" set timeout 5 expect "OK\r\n" {} timeout { exit 1 } print "ATZ -- resetting modem" send -h "ATZ\r" set timeout 30 expect { "OK\r\n" {} timeout { exit 1 } } print {OK -- modem alive} print "$InitString -- initialization string" send -h $InitString send -h "\r" set timeout 12 expect "OK\r\n" {} timeout { exit 1 } print {OK -- modem configured} print "ATDT$PhoneNumber -- dialing" send -h "ATDT" send $PhoneNumber send "\r" set timeout 90 expect {*CONNECT*} { print "\n-----\n$expect_out(buffer)\n-----\n" } \ {*BUSY*} { print "\n-----\n$expect_out(buffer)\n-----\n" exit 1 } \ {*VOICE*} { print "\n-----\n$expect_out(buffer)\n-----\n" exit 1 } \ {*NO CARRIER*} { print "\n-----\n$expect_out(buffer)\n-----\n" exit 1 } \ {*NO ANSWER*} { print "\n-----\n$expect_out(buffer)\n-----\n" exit 1 } \ timeout { print "\n-----\n$expect_out(buffer)\n-----\n" exit 1 } set timeout 20 print "$LoginName -- sending login name" expect {*ogin*} { print "\n-----\n$expect_out(buffer)\n-----\n" send $LoginName send "\r" expect {*assword*} { print "\n-----\n$expect_out(buffer)\n-----\n" } \ timeout { exit 1 } print {... -- sending password} send $Password send "\r" } timeout { exit 1 } exit 0 ______________________________________________________________________ o ip-up This is a very important script, it is what is going to make your eql configuration work automatically whenever ppp devices come up after redialing. ______________________________________________________________________ #!/bin/sh INTERFACE=$1 DEVICE=$2 SPEED=$3 LOCAL_IP=$4 REMOTE_IP=$5 # Load Balancing Configuration For Client Side if [ $LOCAL_IP = "204.180.7.41" ] then # this deletes the route ppp creates, and allows us to load balance # the traffic going directly to that host. /sbin/route del 198.67.33.16 /etc/ppp/eql_enslave eql $INTERFACE $SPEED # This re-adds the route in case its lost for one reason or another. /sbin/route add default dev eql fi ______________________________________________________________________ 3.3. eql's eql_enslave Syntax The syntax for enslaving a device is "eql_enslave ". Here are some example enslavings: ______________________________________________________________________ eql_enslave eql sl0 28800 eql_enslave eql ppp0 14400 eql_enslave eql sl1 57600 ______________________________________________________________________ When you want to free a device from its life of slavery, you can just down the device with ifconfig and the eql master will automatically bury the dead slave and remove it from its scheduling queue. 4. About the Slave Scheduler Algorithm The slave scheduler probably could be replaced with a dozen other things and push traffic much faster. The formula in the current set up of the driver was tuned to handle slaves with wildly different bits-per-second "priorities". All testing I have done was with two 28.8 V.FC modems, one connecting at 28800 bps or slower, and the other connecting at 14400 bps all the time. One version of the scheduler was able to push 5.3 K/s through the 28800 and 14400 connections, but when the priorities on the links were very wide apart (57600 vs. 14400) The "faster" modem received all traffic and the "slower" modem starved. 5. Tester's Reports Some people have experimented with the eql device with newer kernels kernels (than 1.1.75). I have since updated the driver to patch cleanly in newer kernels because of the removal of the old "slave- balancing" driver config option. The latest patch was generated against the v1.2.2 kernel. o _Anarchy_ (aka Alan Cox) reported 117 K/s running eql over two ISDN B channels. Would someone from the U.K. explain what "dead funky" is supposed to mean? o icee from LinuxNET patched 1.1.86 without any rejects and was able to boot the kernel and enslave a couple of ISDN PPP links. 5.1. Randoph Bentson's Test Report From bentson@grieg.seaslug.org Wed Feb 8 19:08:09 1995 Date: Tue, 7 Feb 95 22:57 PST From: Randolph Bentson To: guru@ncm.com Subject: EQL driver tests I have been checking out your eql driver. (Nice work, that!) Although you may already done this performance testing, here are some data I've discovered. Randolph Bentson bentson@grieg.seaslug.org --------------------------------------------------------- A pseudo-device driver, EQL, written by Simon Janes, can be used to bundle multiple SLIP connections into what appears to be a single connection. This allows one to improve dial-up network connectivity gradually, without having to buy expensive DSU/CSU hardware and services. I have done some testing of this software, with two goals in mind: first, to ensure it actually works as described and second, as a method of exercising my device driver. The following performance measurements were derived from a set of SLIP connections run between two Linux systems (1.1.84) using a 486DX2/66 with a Cyclom-8Ys and a 486SLC/40 with a Cyclom-16Y. (Ports 0,1,2,3 were used. A later configuration will distribute port selection across the different Cirrus chips on the boards.) Once a link was established, I timed a binary ftp transfer of 289284 bytes of data. If there were no overhead (packet headers, inter-character and inter-packet delays, etc.) the transfers would take the following times: bits/sec seconds 345600 8.3 234600 12.3 172800 16.7 153600 18.8 76800 37.6 57600 50.2 38400 75.3 28800 100.4 19200 150.6 9600 301.3 A single line running at the lower speeds and with large packets comes to within 2% of this. Performance is limited for the higher speeds (as predicted by the Cirrus databook) to an aggregate of about 160 kbits/sec. The next round of testing will distribute the load across two or more Cirrus chips. The good news is that one gets nearly the full advantage of the second, third, and fourth line's bandwidth. (The bad news is that the connection establishment seemed fragile for the higher speeds. Once established, the connection seemed robust enough.) #lines speed mtu seconds theory actual %of kbit/sec duration speed speed max 3 115200 900 _ 345600 3 115200 400 18.1 345600 159825 46 2 115200 900 _ 230400 2 115200 600 18.1 230400 159825 69 2 115200 400 19.3 230400 149888 65 4 57600 900 _ 234600 4 57600 600 _ 234600 4 57600 400 _ 234600 3 57600 600 20.9 172800 138413 80 3 57600 900 21.2 172800 136455 78 3 115200 600 21.7 345600 133311 38 3 57600 400 22.5 172800 128571 74 4 38400 900 25.2 153600 114795 74 4 38400 600 26.4 153600 109577 71 4 38400 400 27.3 153600 105965 68 2 57600 900 29.1 115200 99410.3 86 1 115200 900 30.7 115200 94229.3 81 2 57600 600 30.2 115200 95789.4 83 3 38400 900 30.3 115200 95473.3 82 3 38400 600 31.2 115200 92719.2 80 1 115200 600 31.3 115200 92423 80 2 57600 400 32.3 115200 89561.6 77 1 115200 400 32.8 115200 88196.3 76 3 38400 400 33.5 115200 86353.4 74 2 38400 900 43.7 76800 66197.7 86 2 38400 600 44 76800 65746.4 85 2 38400 400 47.2 76800 61289 79 4 19200 900 50.8 76800 56945.7 74 4 19200 400 53.2 76800 54376.7 70 4 19200 600 53.7 76800 53870.4 70 1 57600 900 54.6 57600 52982.4 91 1 57600 600 56.2 57600 51474 89 3 19200 900 60.5 57600 47815.5 83 1 57600 400 60.2 57600 48053.8 83 3 19200 600 62 57600 46658.7 81 3 19200 400 64.7 57600 44711.6 77 1 38400 900 79.4 38400 36433.8 94 1 38400 600 82.4 38400 35107.3 91 2 19200 900 84.4 38400 34275.4 89 1 38400 400 86.8 38400 33327.6 86 2 19200 600 87.6 38400 33023.3 85 2 19200 400 91.2 38400 31719.7 82 4 9600 900 94.7 38400 30547.4 79 4 9600 400 106 38400 27290.9 71 4 9600 600 110 38400 26298.5 68 3 9600 900 118 28800 24515.6 85 3 9600 600 120 28800 24107 83 3 9600 400 131 28800 22082.7 76 1 19200 900 155 19200 18663.5 97 1 19200 600 161 19200 17968 93 1 19200 400 170 19200 17016.7 88 2 9600 600 176 19200 16436.6 85 2 9600 900 180 19200 16071.3 83 2 9600 400 181 19200 15982.5 83 1 9600 900 305 9600 9484.72 98 1 9600 600 314 9600 9212.87 95 1 9600 400 332 9600 8713.37 90 5.2. Anthony Healy's Report Date: Mon, 13 Feb 1995 16:17:29 +1100 (EST) From: Antony Healey To: Simon Janes Subject: Re: Load Balancing Hi Simon, I've installed your patch and it works great. I have trialed it over twin SL/IP lines, just over null modems, but I was able to data at over 48Kb/s [ISDN link -Simon]. I managed a transfer of upto 7.5 Kbyte/s on one go, but averaged around 6.4 Kbyte/s, which I think is pretty cool. :) 6. Load Balancing Futures In the future, this driver may no longer be needed, because of proposed extensions to PPP called "Multilink PPP". But now, eql is here, and its here today. Lock and load! :) eql-1.2.ds1/eql_enslave.c0000644000175000017500000000422305737154051014351 0ustar roverrover/* * eql_enslave * * modeled from ifslave.c by Alan Cox (presumably?) * * (c) Copyright 1995 Simon Janes * NCM: Network and Communications Management, Inc. */ #include #include #include #include #include #include #include "/usr/src/linux/drivers/net/eql.h" void check_running(char *device_name); int s; int main(int argc, char **argv) { struct ifreq ifr; slaving_request_t slaving_request; char master_name[16]; int slave_mtu; int master_mtu; if (argc != 4) { fprintf (stderr, "usage: %s \n", argv[0]); exit (1); } strcpy (master_name, argv[1]); strcpy (slaving_request.slave_name, argv[2]); slaving_request.priority = atol (argv[3]); s = socket (AF_INET, SOCK_DGRAM, 0); if ( s == -1) { perror ("socket"); exit (1); } check_running (master_name); check_running (slaving_request.slave_name); strcpy (ifr.ifr_name, slaving_request.slave_name); if (ioctl (s, SIOCGIFMTU, &ifr) == -1) { perror("get MTU on slave failed"); exit (1); } slave_mtu = ifr.ifr_mtu; strcpy (ifr.ifr_name, master_name); if (ioctl (s, SIOCGIFMTU, &ifr) == -1) { perror("get MTU on master failed"); exit (1); } master_mtu = ifr.ifr_mtu; if ( master_mtu != slave_mtu ) { fprintf (stderr, "master (%d) and slave (%d) MTU settings do not match\n", master_mtu, slave_mtu); exit (1); } strcpy (ifr.ifr_name, master_name); ifr.ifr_data = (caddr_t) &slaving_request; if(ioctl (s,EQL_ENSLAVE, &ifr)==-1) { perror("EQL_ENSLAVE failed"); exit (1); } return 0; } void check_running(char *name) { struct ifreq ifr; strcpy (ifr.ifr_name, name); if ( ioctl (s, SIOCGIFFLAGS, &ifr) == -1) { perror (name); exit (1); } if (ifr.ifr_flags & (IFF_RUNNING | IFF_UP) != (IFF_RUNNING | IFF_UP)) { fprintf (stderr, "Device '%s' is not up or running.\n", name); exit (1); } } /* * Local Variables: * compile-command: "gcc -Wall -Wstrict-prototypes -o eql_enslave eql_enslave.c" * End: */