162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Equalizer Load-balancer for serial network interfaces. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes 562306a36Sopenharmony_ci * NCM: Network and Communications Management, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (c) Copyright 2002 David S. Miller (davem@redhat.com) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This software may be used and distributed according to the terms 1062306a36Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The author may be reached as simon@ncm.com, or C/O 1362306a36Sopenharmony_ci * NCM 1462306a36Sopenharmony_ci * Attn: Simon Janes 1562306a36Sopenharmony_ci * 6803 Whittier Ave 1662306a36Sopenharmony_ci * McLean VA 22101 1762306a36Sopenharmony_ci * Phone: 1-703-847-0040 ext 103 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Sources: 2262306a36Sopenharmony_ci * skeleton.c by Donald Becker. 2362306a36Sopenharmony_ci * Inspirations: 2462306a36Sopenharmony_ci * The Harried and Overworked Alan Cox 2562306a36Sopenharmony_ci * Conspiracies: 2662306a36Sopenharmony_ci * The Alan Cox and Mike McLagan plot to get someone else to do the code, 2762306a36Sopenharmony_ci * which turned out to be me. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * $Log: eql.c,v $ 3262306a36Sopenharmony_ci * Revision 1.2 1996/04/11 17:51:52 guru 3362306a36Sopenharmony_ci * Added one-line eql_remove_slave patch. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Revision 1.1 1996/04/11 17:44:17 guru 3662306a36Sopenharmony_ci * Initial revision 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Revision 3.13 1996/01/21 15:17:18 alan 3962306a36Sopenharmony_ci * tx_queue_len changes. 4062306a36Sopenharmony_ci * reformatted. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Revision 3.12 1995/03/22 21:07:51 anarchy 4362306a36Sopenharmony_ci * Added capable() checks on configuration. 4462306a36Sopenharmony_ci * Moved header file. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Revision 3.11 1995/01/19 23:14:31 guru 4762306a36Sopenharmony_ci * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - 4862306a36Sopenharmony_ci * (priority_Bps) + bytes_queued * 8; 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Revision 3.10 1995/01/19 23:07:53 guru 5162306a36Sopenharmony_ci * back to 5262306a36Sopenharmony_ci * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - 5362306a36Sopenharmony_ci * (priority_Bps) + bytes_queued; 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Revision 3.9 1995/01/19 22:38:20 guru 5662306a36Sopenharmony_ci * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - 5762306a36Sopenharmony_ci * (priority_Bps) + bytes_queued * 4; 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Revision 3.8 1995/01/19 22:30:55 guru 6062306a36Sopenharmony_ci * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - 6162306a36Sopenharmony_ci * (priority_Bps) + bytes_queued * 2; 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * Revision 3.7 1995/01/19 21:52:35 guru 6462306a36Sopenharmony_ci * printk's trimmed out. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * Revision 3.6 1995/01/19 21:49:56 guru 6762306a36Sopenharmony_ci * This is working pretty well. I gained 1 K/s in speed.. now it's just 6862306a36Sopenharmony_ci * robustness and printk's to be diked out. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * Revision 3.5 1995/01/18 22:29:59 guru 7162306a36Sopenharmony_ci * still crashes the kernel when the lock_wait thing is woken up. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Revision 3.4 1995/01/18 21:59:47 guru 7462306a36Sopenharmony_ci * Broken set-bit locking snapshot 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Revision 3.3 1995/01/17 22:09:18 guru 7762306a36Sopenharmony_ci * infinite sleep in a lock somewhere.. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Revision 3.2 1995/01/15 16:46:06 guru 8062306a36Sopenharmony_ci * Log trimmed of non-pertinent 1.x branch messages 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Revision 3.1 1995/01/15 14:41:45 guru 8362306a36Sopenharmony_ci * New Scheduler and timer stuff... 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Revision 1.15 1995/01/15 14:29:02 guru 8662306a36Sopenharmony_ci * Will make 1.14 (now 1.15) the 3.0 branch, and the 1.12 the 2.0 branch, the one 8762306a36Sopenharmony_ci * with the dumber scheduler 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Revision 1.14 1995/01/15 02:37:08 guru 9062306a36Sopenharmony_ci * shock.. the kept-new-versions could have zonked working 9162306a36Sopenharmony_ci * stuff.. shudder 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * Revision 1.13 1995/01/15 02:36:31 guru 9462306a36Sopenharmony_ci * big changes 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * scheduler was torn out and replaced with something smarter 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * global names not prefixed with eql_ were renamed to protect 9962306a36Sopenharmony_ci * against namespace collisions 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * a few more abstract interfaces were added to facilitate any 10262306a36Sopenharmony_ci * potential change of datastructure. the driver is still using 10362306a36Sopenharmony_ci * a linked list of slaves. going to a heap would be a bit of 10462306a36Sopenharmony_ci * an overkill. 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * this compiles fine with no warnings. 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * the locking mechanism and timer stuff must be written however, 10962306a36Sopenharmony_ci * this version will not work otherwise 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * Sorry, I had to rewrite most of this for 2.5.x -DaveM 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#include <linux/compat.h> 11762306a36Sopenharmony_ci#include <linux/capability.h> 11862306a36Sopenharmony_ci#include <linux/module.h> 11962306a36Sopenharmony_ci#include <linux/kernel.h> 12062306a36Sopenharmony_ci#include <linux/init.h> 12162306a36Sopenharmony_ci#include <linux/slab.h> 12262306a36Sopenharmony_ci#include <linux/timer.h> 12362306a36Sopenharmony_ci#include <linux/netdevice.h> 12462306a36Sopenharmony_ci#include <net/net_namespace.h> 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#include <linux/if.h> 12762306a36Sopenharmony_ci#include <linux/if_arp.h> 12862306a36Sopenharmony_ci#include <linux/if_eql.h> 12962306a36Sopenharmony_ci#include <linux/pkt_sched.h> 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#include <linux/uaccess.h> 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int eql_open(struct net_device *dev); 13462306a36Sopenharmony_cistatic int eql_close(struct net_device *dev); 13562306a36Sopenharmony_cistatic int eql_siocdevprivate(struct net_device *dev, struct ifreq *ifr, 13662306a36Sopenharmony_ci void __user *data, int cmd); 13762306a36Sopenharmony_cistatic netdev_tx_t eql_slave_xmit(struct sk_buff *skb, struct net_device *dev); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define eql_is_slave(dev) ((dev->flags & IFF_SLAVE) == IFF_SLAVE) 14062306a36Sopenharmony_ci#define eql_is_master(dev) ((dev->flags & IFF_MASTER) == IFF_MASTER) 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void eql_kill_one_slave(slave_queue_t *queue, slave_t *slave); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void eql_timer(struct timer_list *t) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci equalizer_t *eql = from_timer(eql, t, timer); 14762306a36Sopenharmony_ci struct list_head *this, *tmp, *head; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci spin_lock(&eql->queue.lock); 15062306a36Sopenharmony_ci head = &eql->queue.all_slaves; 15162306a36Sopenharmony_ci list_for_each_safe(this, tmp, head) { 15262306a36Sopenharmony_ci slave_t *slave = list_entry(this, slave_t, list); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if ((slave->dev->flags & IFF_UP) == IFF_UP) { 15562306a36Sopenharmony_ci slave->bytes_queued -= slave->priority_Bps; 15662306a36Sopenharmony_ci if (slave->bytes_queued < 0) 15762306a36Sopenharmony_ci slave->bytes_queued = 0; 15862306a36Sopenharmony_ci } else { 15962306a36Sopenharmony_ci eql_kill_one_slave(&eql->queue, slave); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci spin_unlock(&eql->queue.lock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci eql->timer.expires = jiffies + EQL_DEFAULT_RESCHED_IVAL; 16662306a36Sopenharmony_ci add_timer(&eql->timer); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const char version[] __initconst = 17062306a36Sopenharmony_ci "Equalizer2002: Simon Janes (simon@ncm.com) and David S. Miller (davem@redhat.com)"; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic const struct net_device_ops eql_netdev_ops = { 17362306a36Sopenharmony_ci .ndo_open = eql_open, 17462306a36Sopenharmony_ci .ndo_stop = eql_close, 17562306a36Sopenharmony_ci .ndo_siocdevprivate = eql_siocdevprivate, 17662306a36Sopenharmony_ci .ndo_start_xmit = eql_slave_xmit, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void __init eql_setup(struct net_device *dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(dev); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci timer_setup(&eql->timer, eql_timer, 0); 18462306a36Sopenharmony_ci eql->timer.expires = jiffies + EQL_DEFAULT_RESCHED_IVAL; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci spin_lock_init(&eql->queue.lock); 18762306a36Sopenharmony_ci INIT_LIST_HEAD(&eql->queue.all_slaves); 18862306a36Sopenharmony_ci eql->queue.master_dev = dev; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci dev->netdev_ops = &eql_netdev_ops; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * Now we undo some of the things that eth_setup does 19462306a36Sopenharmony_ci * that we don't like 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci dev->mtu = EQL_DEFAULT_MTU; /* set to 576 in if_eql.h */ 19862306a36Sopenharmony_ci dev->flags = IFF_MASTER; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci dev->type = ARPHRD_SLIP; 20162306a36Sopenharmony_ci dev->tx_queue_len = 5; /* Hands them off fast */ 20262306a36Sopenharmony_ci netif_keep_dst(dev); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int eql_open(struct net_device *dev) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(dev); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* XXX We should force this off automatically for the user. */ 21062306a36Sopenharmony_ci netdev_info(dev, 21162306a36Sopenharmony_ci "remember to turn off Van-Jacobson compression on your slave devices\n"); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci BUG_ON(!list_empty(&eql->queue.all_slaves)); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci eql->min_slaves = 1; 21662306a36Sopenharmony_ci eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */ 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci add_timer(&eql->timer); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void eql_kill_one_slave(slave_queue_t *queue, slave_t *slave) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci list_del(&slave->list); 22662306a36Sopenharmony_ci queue->num_slaves--; 22762306a36Sopenharmony_ci slave->dev->flags &= ~IFF_SLAVE; 22862306a36Sopenharmony_ci netdev_put(slave->dev, &slave->dev_tracker); 22962306a36Sopenharmony_ci kfree(slave); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void eql_kill_slave_queue(slave_queue_t *queue) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct list_head *head, *tmp, *this; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci spin_lock_bh(&queue->lock); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci head = &queue->all_slaves; 23962306a36Sopenharmony_ci list_for_each_safe(this, tmp, head) { 24062306a36Sopenharmony_ci slave_t *s = list_entry(this, slave_t, list); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci eql_kill_one_slave(queue, s); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci spin_unlock_bh(&queue->lock); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int eql_close(struct net_device *dev) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(dev); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci * The timer has to be stopped first before we start hacking away 25462306a36Sopenharmony_ci * at the data structure it scans every so often... 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci del_timer_sync(&eql->timer); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci eql_kill_slave_queue(&eql->queue); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int eql_enslave(struct net_device *dev, slaving_request_t __user *srq); 26562306a36Sopenharmony_cistatic int eql_emancipate(struct net_device *dev, slaving_request_t __user *srq); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int eql_g_slave_cfg(struct net_device *dev, slave_config_t __user *sc); 26862306a36Sopenharmony_cistatic int eql_s_slave_cfg(struct net_device *dev, slave_config_t __user *sc); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int eql_g_master_cfg(struct net_device *dev, master_config_t __user *mc); 27162306a36Sopenharmony_cistatic int eql_s_master_cfg(struct net_device *dev, master_config_t __user *mc); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int eql_siocdevprivate(struct net_device *dev, struct ifreq *ifr, 27462306a36Sopenharmony_ci void __user *data, int cmd) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci if (cmd != EQL_GETMASTRCFG && cmd != EQL_GETSLAVECFG && 27762306a36Sopenharmony_ci !capable(CAP_NET_ADMIN)) 27862306a36Sopenharmony_ci return -EPERM; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (in_compat_syscall()) /* to be implemented */ 28162306a36Sopenharmony_ci return -EOPNOTSUPP; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci switch (cmd) { 28462306a36Sopenharmony_ci case EQL_ENSLAVE: 28562306a36Sopenharmony_ci return eql_enslave(dev, data); 28662306a36Sopenharmony_ci case EQL_EMANCIPATE: 28762306a36Sopenharmony_ci return eql_emancipate(dev, data); 28862306a36Sopenharmony_ci case EQL_GETSLAVECFG: 28962306a36Sopenharmony_ci return eql_g_slave_cfg(dev, data); 29062306a36Sopenharmony_ci case EQL_SETSLAVECFG: 29162306a36Sopenharmony_ci return eql_s_slave_cfg(dev, data); 29262306a36Sopenharmony_ci case EQL_GETMASTRCFG: 29362306a36Sopenharmony_ci return eql_g_master_cfg(dev, data); 29462306a36Sopenharmony_ci case EQL_SETMASTRCFG: 29562306a36Sopenharmony_ci return eql_s_master_cfg(dev, data); 29662306a36Sopenharmony_ci default: 29762306a36Sopenharmony_ci return -EOPNOTSUPP; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* queue->lock must be held */ 30262306a36Sopenharmony_cistatic slave_t *__eql_schedule_slaves(slave_queue_t *queue) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci unsigned long best_load = ~0UL; 30562306a36Sopenharmony_ci struct list_head *this, *tmp, *head; 30662306a36Sopenharmony_ci slave_t *best_slave; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci best_slave = NULL; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Make a pass to set the best slave. */ 31162306a36Sopenharmony_ci head = &queue->all_slaves; 31262306a36Sopenharmony_ci list_for_each_safe(this, tmp, head) { 31362306a36Sopenharmony_ci slave_t *slave = list_entry(this, slave_t, list); 31462306a36Sopenharmony_ci unsigned long slave_load, bytes_queued, priority_Bps; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Go through the slave list once, updating best_slave 31762306a36Sopenharmony_ci * whenever a new best_load is found. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci bytes_queued = slave->bytes_queued; 32062306a36Sopenharmony_ci priority_Bps = slave->priority_Bps; 32162306a36Sopenharmony_ci if ((slave->dev->flags & IFF_UP) == IFF_UP) { 32262306a36Sopenharmony_ci slave_load = (~0UL - (~0UL / 2)) - 32362306a36Sopenharmony_ci (priority_Bps) + bytes_queued * 8; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (slave_load < best_load) { 32662306a36Sopenharmony_ci best_load = slave_load; 32762306a36Sopenharmony_ci best_slave = slave; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci /* We found a dead slave, kill it. */ 33162306a36Sopenharmony_ci eql_kill_one_slave(queue, slave); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci return best_slave; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic netdev_tx_t eql_slave_xmit(struct sk_buff *skb, struct net_device *dev) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(dev); 34062306a36Sopenharmony_ci slave_t *slave; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci spin_lock(&eql->queue.lock); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci slave = __eql_schedule_slaves(&eql->queue); 34562306a36Sopenharmony_ci if (slave) { 34662306a36Sopenharmony_ci struct net_device *slave_dev = slave->dev; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci skb->dev = slave_dev; 34962306a36Sopenharmony_ci skb->priority = TC_PRIO_FILLER; 35062306a36Sopenharmony_ci slave->bytes_queued += skb->len; 35162306a36Sopenharmony_ci dev_queue_xmit(skb); 35262306a36Sopenharmony_ci dev->stats.tx_packets++; 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci dev->stats.tx_dropped++; 35562306a36Sopenharmony_ci dev_kfree_skb(skb); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci spin_unlock(&eql->queue.lock); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return NETDEV_TX_OK; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* 36462306a36Sopenharmony_ci * Private ioctl functions 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* queue->lock must be held */ 36862306a36Sopenharmony_cistatic slave_t *__eql_find_slave_dev(slave_queue_t *queue, struct net_device *dev) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct list_head *this, *head; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci head = &queue->all_slaves; 37362306a36Sopenharmony_ci list_for_each(this, head) { 37462306a36Sopenharmony_ci slave_t *slave = list_entry(this, slave_t, list); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (slave->dev == dev) 37762306a36Sopenharmony_ci return slave; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return NULL; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic inline int eql_is_full(slave_queue_t *queue) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(queue->master_dev); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (queue->num_slaves >= eql->max_slaves) 38862306a36Sopenharmony_ci return 1; 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* queue->lock must be held */ 39362306a36Sopenharmony_cistatic int __eql_insert_slave(slave_queue_t *queue, slave_t *slave) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci if (!eql_is_full(queue)) { 39662306a36Sopenharmony_ci slave_t *duplicate_slave = NULL; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci duplicate_slave = __eql_find_slave_dev(queue, slave->dev); 39962306a36Sopenharmony_ci if (duplicate_slave) 40062306a36Sopenharmony_ci eql_kill_one_slave(queue, duplicate_slave); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci netdev_hold(slave->dev, &slave->dev_tracker, GFP_ATOMIC); 40362306a36Sopenharmony_ci list_add(&slave->list, &queue->all_slaves); 40462306a36Sopenharmony_ci queue->num_slaves++; 40562306a36Sopenharmony_ci slave->dev->flags |= IFF_SLAVE; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return -ENOSPC; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int eql_enslave(struct net_device *master_dev, slaving_request_t __user *srqp) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct net_device *slave_dev; 41662306a36Sopenharmony_ci slaving_request_t srq; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (copy_from_user(&srq, srqp, sizeof (slaving_request_t))) 41962306a36Sopenharmony_ci return -EFAULT; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci slave_dev = __dev_get_by_name(&init_net, srq.slave_name); 42262306a36Sopenharmony_ci if (!slave_dev) 42362306a36Sopenharmony_ci return -ENODEV; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if ((master_dev->flags & IFF_UP) == IFF_UP) { 42662306a36Sopenharmony_ci /* slave is not a master & not already a slave: */ 42762306a36Sopenharmony_ci if (!eql_is_master(slave_dev) && !eql_is_slave(slave_dev)) { 42862306a36Sopenharmony_ci slave_t *s = kzalloc(sizeof(*s), GFP_KERNEL); 42962306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(master_dev); 43062306a36Sopenharmony_ci int ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!s) 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci s->dev = slave_dev; 43662306a36Sopenharmony_ci s->priority = srq.priority; 43762306a36Sopenharmony_ci s->priority_bps = srq.priority; 43862306a36Sopenharmony_ci s->priority_Bps = srq.priority / 8; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci spin_lock_bh(&eql->queue.lock); 44162306a36Sopenharmony_ci ret = __eql_insert_slave(&eql->queue, s); 44262306a36Sopenharmony_ci if (ret) 44362306a36Sopenharmony_ci kfree(s); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci spin_unlock_bh(&eql->queue.lock); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int eql_emancipate(struct net_device *master_dev, slaving_request_t __user *srqp) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(master_dev); 45762306a36Sopenharmony_ci struct net_device *slave_dev; 45862306a36Sopenharmony_ci slaving_request_t srq; 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (copy_from_user(&srq, srqp, sizeof (slaving_request_t))) 46262306a36Sopenharmony_ci return -EFAULT; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci slave_dev = __dev_get_by_name(&init_net, srq.slave_name); 46562306a36Sopenharmony_ci if (!slave_dev) 46662306a36Sopenharmony_ci return -ENODEV; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci ret = -EINVAL; 46962306a36Sopenharmony_ci spin_lock_bh(&eql->queue.lock); 47062306a36Sopenharmony_ci if (eql_is_slave(slave_dev)) { 47162306a36Sopenharmony_ci slave_t *slave = __eql_find_slave_dev(&eql->queue, slave_dev); 47262306a36Sopenharmony_ci if (slave) { 47362306a36Sopenharmony_ci eql_kill_one_slave(&eql->queue, slave); 47462306a36Sopenharmony_ci ret = 0; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci spin_unlock_bh(&eql->queue.lock); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int eql_g_slave_cfg(struct net_device *dev, slave_config_t __user *scp) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci equalizer_t *eql = netdev_priv(dev); 48562306a36Sopenharmony_ci slave_t *slave; 48662306a36Sopenharmony_ci struct net_device *slave_dev; 48762306a36Sopenharmony_ci slave_config_t sc; 48862306a36Sopenharmony_ci int ret; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (copy_from_user(&sc, scp, sizeof (slave_config_t))) 49162306a36Sopenharmony_ci return -EFAULT; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci slave_dev = __dev_get_by_name(&init_net, sc.slave_name); 49462306a36Sopenharmony_ci if (!slave_dev) 49562306a36Sopenharmony_ci return -ENODEV; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ret = -EINVAL; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci spin_lock_bh(&eql->queue.lock); 50062306a36Sopenharmony_ci if (eql_is_slave(slave_dev)) { 50162306a36Sopenharmony_ci slave = __eql_find_slave_dev(&eql->queue, slave_dev); 50262306a36Sopenharmony_ci if (slave) { 50362306a36Sopenharmony_ci sc.priority = slave->priority; 50462306a36Sopenharmony_ci ret = 0; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci spin_unlock_bh(&eql->queue.lock); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (!ret && copy_to_user(scp, &sc, sizeof (slave_config_t))) 51062306a36Sopenharmony_ci ret = -EFAULT; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return ret; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int eql_s_slave_cfg(struct net_device *dev, slave_config_t __user *scp) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci slave_t *slave; 51862306a36Sopenharmony_ci equalizer_t *eql; 51962306a36Sopenharmony_ci struct net_device *slave_dev; 52062306a36Sopenharmony_ci slave_config_t sc; 52162306a36Sopenharmony_ci int ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (copy_from_user(&sc, scp, sizeof (slave_config_t))) 52462306a36Sopenharmony_ci return -EFAULT; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci slave_dev = __dev_get_by_name(&init_net, sc.slave_name); 52762306a36Sopenharmony_ci if (!slave_dev) 52862306a36Sopenharmony_ci return -ENODEV; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ret = -EINVAL; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci eql = netdev_priv(dev); 53362306a36Sopenharmony_ci spin_lock_bh(&eql->queue.lock); 53462306a36Sopenharmony_ci if (eql_is_slave(slave_dev)) { 53562306a36Sopenharmony_ci slave = __eql_find_slave_dev(&eql->queue, slave_dev); 53662306a36Sopenharmony_ci if (slave) { 53762306a36Sopenharmony_ci slave->priority = sc.priority; 53862306a36Sopenharmony_ci slave->priority_bps = sc.priority; 53962306a36Sopenharmony_ci slave->priority_Bps = sc.priority / 8; 54062306a36Sopenharmony_ci ret = 0; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci spin_unlock_bh(&eql->queue.lock); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return ret; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int eql_g_master_cfg(struct net_device *dev, master_config_t __user *mcp) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci equalizer_t *eql; 55162306a36Sopenharmony_ci master_config_t mc; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci memset(&mc, 0, sizeof(master_config_t)); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (eql_is_master(dev)) { 55662306a36Sopenharmony_ci eql = netdev_priv(dev); 55762306a36Sopenharmony_ci mc.max_slaves = eql->max_slaves; 55862306a36Sopenharmony_ci mc.min_slaves = eql->min_slaves; 55962306a36Sopenharmony_ci if (copy_to_user(mcp, &mc, sizeof (master_config_t))) 56062306a36Sopenharmony_ci return -EFAULT; 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci return -EINVAL; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int eql_s_master_cfg(struct net_device *dev, master_config_t __user *mcp) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci equalizer_t *eql; 56962306a36Sopenharmony_ci master_config_t mc; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (copy_from_user(&mc, mcp, sizeof (master_config_t))) 57262306a36Sopenharmony_ci return -EFAULT; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (eql_is_master(dev)) { 57562306a36Sopenharmony_ci eql = netdev_priv(dev); 57662306a36Sopenharmony_ci eql->max_slaves = mc.max_slaves; 57762306a36Sopenharmony_ci eql->min_slaves = mc.min_slaves; 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic struct net_device *dev_eql; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int __init eql_init_module(void) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci int err; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci pr_info("%s\n", version); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci dev_eql = alloc_netdev(sizeof(equalizer_t), "eql", NET_NAME_UNKNOWN, 59262306a36Sopenharmony_ci eql_setup); 59362306a36Sopenharmony_ci if (!dev_eql) 59462306a36Sopenharmony_ci return -ENOMEM; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci err = register_netdev(dev_eql); 59762306a36Sopenharmony_ci if (err) 59862306a36Sopenharmony_ci free_netdev(dev_eql); 59962306a36Sopenharmony_ci return err; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void __exit eql_cleanup_module(void) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci unregister_netdev(dev_eql); 60562306a36Sopenharmony_ci free_netdev(dev_eql); 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cimodule_init(eql_init_module); 60962306a36Sopenharmony_cimodule_exit(eql_cleanup_module); 61062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 611