/* * Equalizer Load-balancer for serial network interfaces. * * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes * NCM: Network and Communications Management, Inc. * * * This software may be used and distributed according to the terms * of the GNU General 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 */ /* * Sources: * skeleton.c by Donald Becker. * Inspirations: * The Harried and Overworked Alan Cox * Conspiracies: * The Alan Cox and Mike McLagan plot to get someone else to do the code, * which turned out to be me. */ /* * $Log: eql.c,v $ * Revision 1.1.1.1 2003/06/23 22:18:28 jharrell * Imported kernel sources * * Revision 1.1.1.1 2002/03/18 15:11:55 jharrell * 2.4.17 sherman import * * Revision 1.2 1996/04/11 17:51:52 guru * Added one-line eql_remove_slave patch. * * Revision 1.1 1996/04/11 17:44:17 guru * Initial revision * * Revision 3.13 1996/01/21 15:17:18 alan * tx_queue_len changes. * reformatted. * * Revision 3.12 1995/03/22 21:07:51 anarchy * Added capable() checks on configuration. * Moved header file. * * 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 it's 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-pertinent 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 static char version[] __initdata = "Equalizer1996: $Revision: 1.1.1.1 $ $Date: 2003/06/23 22:18:28 $ Simon Janes (simon@ncm.com)\n"; #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; static int eql_init(struct net_device *dev); /* */ static int eql_open(struct net_device *dev); /* */ static int eql_close(struct net_device *dev); /* */ static int eql_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); /* */ static int eql_slave_xmit(struct sk_buff *skb, struct net_device *dev); /* */ static struct net_device_stats *eql_get_stats(struct net_device *dev); /* */ /* ioctl() handlers ---------------- */ static int eql_enslave(struct net_device *dev, slaving_request_t *srq); /* */ static int eql_emancipate(struct net_device *dev, slaving_request_t *srq); /* */ static int eql_g_slave_cfg(struct net_device *dev, slave_config_t *sc); /* */ static int eql_s_slave_cfg(struct net_device *dev, slave_config_t *sc); /* */ static int eql_g_master_cfg(struct net_device *dev, master_config_t *mc); /* */ static int eql_s_master_cfg(struct net_device *dev, master_config_t *mc); /* */ static inline int eql_is_slave(struct net_device *dev); /* */ static inline int eql_is_master(struct net_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 net_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 net_device *dev); -* */ static int eql_remove_slave_dev(slave_queue_t *queue, struct net_device *dev); /* */ static inline struct net_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 net_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 net_device * interface functions --------------------------------------------------------- */ static int __init eql_init(struct net_device *dev) { static unsigned version_printed; /* static unsigned num_masters = 0; */ equalizer_t *eql = 0; SET_MODULE_OWNER(dev); if ( version_printed++ == 0 && eql_debug > 0) printk(version); /* * Initialize the device structure. */ dev->priv = kmalloc (sizeof (equalizer_t), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset (dev->priv, 0, sizeof (equalizer_t)); eql = (equalizer_t *) dev->priv; eql->stats = kmalloc (sizeof (struct net_device_stats), GFP_KERNEL); if (eql->stats == NULL) { kfree(dev->priv); dev->priv = NULL; return -ENOMEM; } memset (eql->stats, 0, sizeof (struct net_device_stats)); init_timer (&eql->timer); eql->timer.data = (unsigned long) dev->priv; eql->timer.expires = jiffies+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 * eql-generic values. */ /* * 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->type = ARPHRD_SLIP; dev->tx_queue_len = 5; /* Hands them off fast */ return 0; } static int eql_open(struct net_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 printk ("%s: remember to turn off Van-Jacobson compression on your slave devices.\n", dev->name); 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 -ENOMEM; } static int eql_close(struct net_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... */ #ifdef EQL_DEBUG printk ("%s: stopping timer\n", dev->name); #endif eql->timer_on = 0; del_timer (&eql->timer); eql_delete_slave_queue (eql->queue); return 0; } static int eql_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { if(cmd!=EQL_GETMASTRCFG && cmd!=EQL_GETSLAVECFG && !capable(CAP_NET_ADMIN)) return -EPERM; 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 net_device *dev) { equalizer_t *eql = (equalizer_t *) dev->priv; struct net_device *slave_dev = 0; slave_t *slave; if (skb == NULL) return 0; eql_schedule_slaves (eql->queue); slave = eql_best_slave (eql->queue); slave_dev = slave ? slave->dev : 0; if ( slave_dev != 0 ) { #ifdef EQL_DEBUG if (eql_debug >= 100) printk ("%s: %d slaves xmitng %d B %s\n", dev->name, eql_number_slaves (eql->queue), skb->len, slave_dev->name); #endif skb->dev = slave_dev; skb->priority = 1; slave->bytes_queued += skb->len; dev_queue_xmit (skb); eql->stats->tx_packets++; } 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); } return 0; } static struct net_device_stats * eql_get_stats(struct net_device *dev) { equalizer_t *eql = (equalizer_t *) dev->priv; return eql->stats; } /* * Private ioctl functions */ static int eql_enslave(struct net_device *dev, slaving_request_t *srqp) { struct net_device *master_dev; struct net_device *slave_dev; slaving_request_t srq; if (copy_from_user(&srq, srqp, sizeof (slaving_request_t))) { #ifdef EQL_DEBUG if (eql_debug >= 20) printk ("EQL enslave: error detected by copy_from_user\n"); #endif return -EFAULT; } #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_by_name (srq.slave_name); if (master_dev != 0 && slave_dev != 0) { if ((master_dev->flags & IFF_UP) == IFF_UP) { /*slave is not a master & not already a slave:*/ if (! eql_is_master (slave_dev) && ! eql_is_slave (slave_dev) ) { slave_t *s = eql_new_slave (); equalizer_t *eql = (equalizer_t *) master_dev->priv; if (!s) return -ENOMEM; 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; } #ifdef EQL_DEBUG else if (eql_debug >= 20) printk ("EQL enslave: slave is master or slave is already slave\n"); #endif } #ifdef EQL_DEBUG else if (eql_debug >= 20) printk ("EQL enslave: master device not up!\n"); #endif } #ifdef EQL_DEBUG else if (eql_debug >= 20) printk ("EQL enslave: master or slave are NULL"); #endif return -EINVAL; } static int eql_emancipate(struct net_device *dev, slaving_request_t *srqp) { struct net_device *master_dev; struct net_device *slave_dev; slaving_request_t srq; if (copy_from_user(&srq, srqp, sizeof (slaving_request_t))) return -EFAULT; #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_by_name (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 net_device *dev, slave_config_t *scp) { slave_t *slave; equalizer_t *eql; struct net_device *slave_dev; slave_config_t sc; if (copy_from_user (&sc, scp, sizeof (slave_config_t))) return -EFAULT; #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_by_name (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; if (copy_to_user (scp, &sc, sizeof (slave_config_t))) return -EFAULT; return 0; } } return -EINVAL; } static int eql_s_slave_cfg(struct net_device *dev, slave_config_t *scp) { slave_t *slave; equalizer_t *eql; struct net_device *slave_dev; slave_config_t sc; if (copy_from_user (&sc, scp, sizeof (slave_config_t))) return -EFAULT; #ifdef EQL_DEBUG if (eql_debug >= 20) printk ("%s: set config for slave `%s'\n", dev->name, sc.slave_name); #endif eql = (equalizer_t *) dev->priv; slave_dev = __dev_get_by_name (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 net_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; if (copy_to_user (mcp, &mc, sizeof (master_config_t))) return -EFAULT; return 0; } return -EINVAL; } static int eql_s_master_cfg(struct net_device *dev, master_config_t *mcp) { equalizer_t *eql; master_config_t mc; if (copy_from_user (&mc, mcp, sizeof (master_config_t))) return -EFAULT; #if EQL_DEBUG if (eql_debug >= 20) printk ("%s: set master config\n", dev->name); #endif 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 net_device *dev) { if (dev) { if ((dev->flags & IFF_SLAVE) == IFF_SLAVE) return 1; } return 0; } static inline int eql_is_master(struct net_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; } static void eql_delete_slave(slave_t *slave) { kfree (slave); } #if 0 /* not currently used, will be used when we really 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 net_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); if (!queue) goto err_out; head_slave = eql_new_slave (); if (!head_slave) goto err_out_queue; tail_slave = eql_new_slave (); if (!tail_slave) goto err_out_hs; memset (queue, 0, sizeof (slave_queue_t)); head_slave->next = tail_slave; tail_slave->next = 0; queue->head = head_slave; queue->num_slaves = 0; queue->master_dev = dev; return queue; err_out_hs: kfree (head_slave); err_out_queue: kfree (queue); err_out: return NULL; } 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 periodically.. * 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) { unsigned long flags; save_flags(flags); 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++; restore_flags(flags); return 0; } restore_flags(flags); return 1; } static slave_t *eql_remove_slave(slave_queue_t *queue, slave_t *slave) { slave_t *prev; slave_t *curr; unsigned long flags; save_flags(flags); cli (); prev = queue->head; curr = queue->head->next; while (curr != slave && curr->dev != 0 ) { /* printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */ prev = curr; curr = curr->next; } if (curr == slave) { prev->next = curr->next; queue->num_slaves--; curr->dev->flags = curr->dev->flags & ~IFF_SLAVE; restore_flags(flags); return curr; } restore_flags(flags); return 0; /* not found */ } static int eql_remove_slave_dev(slave_queue_t *queue, struct net_device *dev) { slave_t *prev; slave_t *curr; slave_t *target; target = eql_find_slave_dev (queue, dev); if (target != 0) { unsigned long flags; save_flags(flags); cli (); prev = queue->head; curr = prev->next; while (curr != target) { prev = curr; curr = curr->next; } prev->next = curr->next; queue->num_slaves--; restore_flags(flags); eql_delete_slave (curr); return 0; } return 1; } static inline struct net_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 net_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; unsigned long flags; int i; save_flags(flags); 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 */ restore_flags(flags); 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 net_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; } static void eql_timer(unsigned long param) { equalizer_t *eql = (equalizer_t *) param; slave_t *slave; slave_t *slave_corpse = 0; int i; unsigned long flags; if ( ! eql_is_empty (eql->queue) ) { save_flags(flags); 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; } } restore_flags(flags); 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 = jiffies+EQL_DEFAULT_RESCHED_IVAL; add_timer (&eql->timer); } } static struct net_device dev_eql; static int __init eql_init_module(void) { strcpy(dev_eql.name, "eql"); dev_eql.init = eql_init; if (register_netdev(&dev_eql) != 0) { printk("eql: register_netdev() returned non-zero.\n"); return -EIO; } return 0; } static void __exit eql_cleanup_module(void) { kfree(((equalizer_t *)dev_eql.priv)->stats ); kfree(dev_eql.priv); unregister_netdev(&dev_eql); } module_init(eql_init_module); module_exit(eql_cleanup_module); MODULE_LICENSE("GPL"); /* * 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: */