📄 shaper.c
字号:
/* * Simple traffic shaper for Linux NET3. * * (c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved. * http://www.cymru.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide * warranty for any of this software. This material is provided * "AS-IS" and at no charge. * * * Algorithm: * * Queue Frame: * Compute time length of frame at regulated speed * Add frame to queue at appropriate point * Adjust time length computation for followup frames * Any frame that falls outside of its boundaries is freed * * We work to the following constants * * SHAPER_QLEN Maximum queued frames * SHAPER_LATENCY Bounding latency on a frame. Leaving this latency * window drops the frame. This stops us queueing * frames for a long time and confusing a remote * host. * SHAPER_MAXSLIP Maximum time a priority frame may jump forward. * That bounds the penalty we will inflict on low * priority traffic. * SHAPER_BURST Time range we call "now" in order to reduce * system load. The more we make this the burstier * the behaviour, the better local performance you * get through packet clustering on routers and the * worse the remote end gets to judge rtts. * * This is designed to handle lower speed links ( < 200K/second or so). We * run off a 100-150Hz base clock typically. This gives us a resolution at * 200Kbit/second of about 2Kbit or 256 bytes. Above that our timer * resolution may start to cause much more burstiness in the traffic. We * could avoid a lot of that by calling kick_shaper() at the end of the * tied device transmissions. If you run above about 100K second you * may need to tune the supposed speed rate for the right values. * * BUGS: * Downing the interface under the shaper before the shaper * will render your machine defunct. Don't for now shape over * PPP or SLIP therefore! * This will be fixed in BETA4 */ /* * bh_atomic() SMP races fixes and rewritten the locking code to be SMP safe * and irq-mask friendly. NOTE: we can't use start_bh_atomic() in kick_shaper() * because it's going to be recalled from an irq handler, and synchronize_bh() * is a nono if called from irq context. * 1999 Andrea Arcangeli */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/ptrace.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/malloc.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/if_arp.h>#include <linux/init.h>#include <net/dst.h>#include <net/arp.h>#include <linux/if_shaper.h>int sh_debug; /* Debug flag */#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.04 for Linux 2.1\n"/* * Locking */ static int shaper_lock(struct shaper *sh){ /* * Lock in an interrupt must fail */ while (test_and_set_bit(0, &sh->locked)) { if (!in_interrupt()) sleep_on(&sh->wait_queue); else return 0; } return 1;}static void shaper_kick(struct shaper *sh);static void shaper_unlock(struct shaper *sh){ clear_bit(0, &sh->locked); wake_up(&sh->wait_queue); shaper_kick(sh);}/* * Compute clocks on a buffer */ static int shaper_clocks(struct shaper *shaper, struct sk_buff *skb){ int t=skb->len/shaper->bytespertick; return t;}/* * Set the speed of a shaper. We compute this in bytes per tick since * thats how the machine wants to run. Quoted input is in bits per second * as is traditional (note not BAUD). We assume 8 bit bytes. */ static void shaper_setspeed(struct shaper *shaper, int bitspersec){ shaper->bitspersec=bitspersec; shaper->bytespertick=(bitspersec/HZ)/8; if(!shaper->bytespertick) shaper->bytespertick++;}/* * Throw a frame at a shaper. */ static int shaper_qframe(struct shaper *shaper, struct sk_buff *skb){ struct sk_buff *ptr; /* * Get ready to work on this shaper. Lock may fail if its * an interrupt and locked. */ if(!shaper_lock(shaper)) return -1; ptr=shaper->sendq.prev; /* * Set up our packet details */ skb->shapelatency=0; skb->shapeclock=shaper->recovery; if(time_before(skb->shapeclock, jiffies)) skb->shapeclock=jiffies; skb->priority=0; /* short term bug fix */ skb->shapestamp=jiffies; /* * Time slots for this packet. */ skb->shapelen= shaper_clocks(shaper,skb); #ifdef SHAPER_COMPLEX /* and broken.. */ while(ptr && ptr!=(struct sk_buff *)&shaper->sendq) { if(ptr->pri<skb->pri && jiffies - ptr->shapeclock < SHAPER_MAXSLIP) { struct sk_buff *tmp=ptr->prev; /* * It goes before us therefore we slip the length * of the new frame. */ ptr->shapeclock+=skb->shapelen; ptr->shapelatency+=skb->shapelen; /* * The packet may have slipped so far back it * fell off. */ if(ptr->shapelatency > SHAPER_LATENCY) { skb_unlink(ptr); dev_kfree_skb(ptr); } ptr=tmp; } else break; } if(ptr==NULL || ptr==(struct sk_buff *)&shaper->sendq) skb_queue_head(&shaper->sendq,skb); else { struct sk_buff *tmp; /* * Set the packet clock out time according to the * frames ahead. Im sure a bit of thought could drop * this loop. */ for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && tmp!=ptr; tmp=tmp->next) skb->shapeclock+=tmp->shapelen; skb_append(ptr,skb); }#else { struct sk_buff *tmp; /* * Up our shape clock by the time pending on the queue * (Should keep this in the shaper as a variable..) */ for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && tmp!=(struct sk_buff *)&shaper->sendq; tmp=tmp->next) skb->shapeclock+=tmp->shapelen; /* * Queue over time. Spill packet. */ if(skb->shapeclock-jiffies > SHAPER_LATENCY) dev_kfree_skb(skb); else skb_queue_tail(&shaper->sendq, skb); }#endif if(sh_debug) printk("Frame queued.\n"); if(skb_queue_len(&shaper->sendq)>SHAPER_QLEN) { ptr=skb_dequeue(&shaper->sendq); dev_kfree_skb(ptr); } shaper_unlock(shaper); return 0;}/* * Transmit from a shaper */ static void shaper_queue_xmit(struct shaper *shaper, struct sk_buff *skb){ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); if(sh_debug) printk("Kick frame on %p\n",newskb); if(newskb) { newskb->dev=shaper->dev; newskb->priority=2; if(sh_debug) printk("Kick new frame to %s, %d\n", shaper->dev->name,newskb->priority); dev_queue_xmit(newskb); if(sh_debug) printk("Kicked new frame out.\n"); dev_kfree_skb(skb); }}/* * Timer handler for shaping clock */ static void shaper_timer(unsigned long data){ struct shaper *sh=(struct shaper *)data; shaper_kick(sh);}/* * Kick a shaper queue and try and do something sensible with the * queue. */static void shaper_kick(struct shaper *shaper){ struct sk_buff *skb; /* * Shaper unlock will kick */ if (test_and_set_bit(0, &shaper->locked)) { if(sh_debug) printk("Shaper locked.\n"); mod_timer(&shaper->timer, jiffies); return; } /* * Walk the list (may be empty) */ while((skb=skb_peek(&shaper->sendq))!=NULL) { /* * Each packet due to go out by now (within an error * of SHAPER_BURST) gets kicked onto the link */ if(sh_debug) printk("Clock = %d, jiffies = %ld\n", skb->shapeclock, jiffies); if(time_before_eq(skb->shapeclock - jiffies, SHAPER_BURST)) { /* * Pull the frame and get interrupts back on. */ skb_unlink(skb); if (shaper->recovery < skb->shapeclock + skb->shapelen) shaper->recovery = skb->shapeclock + skb->shapelen; /* * Pass on to the physical target device via * our low level packet thrower. */ skb->shapepend=0; shaper_queue_xmit(shaper, skb); /* Fire */ } else break; } /* * Next kick. */ if(skb!=NULL) mod_timer(&shaper->timer, skb->shapeclock); clear_bit(0, &shaper->locked);}/* * Flush the shaper queues on a closedown */ static void shaper_flush(struct shaper *shaper){ struct sk_buff *skb; if(!shaper_lock(shaper)) { printk(KERN_ERR "shaper: shaper_flush() called by an irq!\n"); return; } while((skb=skb_dequeue(&shaper->sendq))!=NULL) dev_kfree_skb(skb); shaper_unlock(shaper);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -