shaper.c

来自「linux 内核源代码」· C语言 代码 · 共 604 行

C
604
字号
/* *			Simple traffic shaper for Linux NET3. * *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. *				http://www.redhat.com * *	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 * * Update History : * *              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 * *              Device statistics (tx_pakets, tx_bytes, *              tx_drops: queue_over_time and collisions: max_queue_exceded) *                               1999/06/18 Jordi Murgo <savage@apostols.org> * *		Use skb->cb for private data. *				 2000/03 Andi Kleen */#include <linux/module.h>#include <linux/kernel.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/slab.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 <linux/if_shaper.h>#include <linux/jiffies.h>#include <net/dst.h>#include <net/arp.h>#include <net/net_namespace.h>struct shaper_cb {	unsigned long	shapeclock;		/* Time it should go out */	unsigned long	shapestamp;		/* Stamp for shaper    */	__u32		shapelatency;		/* Latency on frame */	__u32		shapelen;		/* Frame length in clocks */	__u16		shapepend;		/* Pending */};#define SHAPERCB(skb) ((struct shaper_cb *) ((skb)->cb))static int sh_debug;		/* Debug flag */#define SHAPER_BANNER	"CymruNet Traffic Shaper BETA 0.04 for Linux 2.1\n"static void shaper_kick(struct shaper *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_start_xmit(struct sk_buff *skb, struct net_device *dev){	struct shaper *shaper = dev->priv; 	struct sk_buff *ptr;	spin_lock(&shaper->lock); 	ptr=shaper->sendq.prev; 	/* 	 *	Set up our packet details 	 */ 	SHAPERCB(skb)->shapelatency=0; 	SHAPERCB(skb)->shapeclock=shaper->recovery; 	if(time_before(SHAPERCB(skb)->shapeclock, jiffies)) 		SHAPERCB(skb)->shapeclock=jiffies; 	skb->priority=0;	/* short term bug fix */ 	SHAPERCB(skb)->shapestamp=jiffies; 	/* 	 *	Time slots for this packet. 	 */ 	SHAPERCB(skb)->shapelen= shaper_clocks(shaper,skb);	{		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)			SHAPERCB(skb)->shapeclock+=SHAPERCB(tmp)->shapelen;		/*		 *	Queue over time. Spill packet.		 */		if(time_after(SHAPERCB(skb)->shapeclock,jiffies + SHAPER_LATENCY)) {			dev_kfree_skb(skb);			dev->stats.tx_dropped++;		} else			skb_queue_tail(&shaper->sendq, skb);	}	if(sh_debug) 		printk("Frame queued.\n"); 	if(skb_queue_len(&shaper->sendq)>SHAPER_QLEN) 	{ 		ptr=skb_dequeue(&shaper->sendq);                dev_kfree_skb(ptr);                dev->stats.collisions++; 	}	shaper_kick(shaper);	spin_unlock(&shaper->lock); 	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);                shaper->dev->stats.tx_bytes += skb->len;		shaper->dev->stats.tx_packets++;                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 *shaper = (struct shaper *)data;	spin_lock(&shaper->lock);	shaper_kick(shaper);	spin_unlock(&shaper->lock);}/* *	Kick a shaper queue and try and do something sensible with the *	queue. */static void shaper_kick(struct shaper *shaper){	struct sk_buff *skb;	/*	 *	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 = %ld, jiffies = %ld\n", SHAPERCB(skb)->shapeclock, jiffies);		if(time_before_eq(SHAPERCB(skb)->shapeclock, jiffies + SHAPER_BURST))		{			/*			 *	Pull the frame and get interrupts back on.			 */			skb_unlink(skb, &shaper->sendq);			if (shaper->recovery <			    SHAPERCB(skb)->shapeclock + SHAPERCB(skb)->shapelen)				shaper->recovery = SHAPERCB(skb)->shapeclock + SHAPERCB(skb)->shapelen;			/*			 *	Pass on to the physical target device via			 *	our low level packet thrower.			 */			SHAPERCB(skb)->shapepend=0;			shaper_queue_xmit(shaper, skb);	/* Fire */		}		else			break;	}	/*	 *	Next kick.	 */	if(skb!=NULL)		mod_timer(&shaper->timer, SHAPERCB(skb)->shapeclock);}/* *	Bring the interface up. We just disallow this until a *	bind. */static int shaper_open(struct net_device *dev){	struct shaper *shaper=dev->priv;	/*	 *	Can't open until attached.	 *	Also can't open until speed is set, or we'll get	 *	a division by zero.	 */	if(shaper->dev==NULL)		return -ENODEV;	if(shaper->bitspersec==0)		return -EINVAL;	return 0;}/* *	Closing a shaper flushes the queues. */static int shaper_close(struct net_device *dev){	struct shaper *shaper=dev->priv;	struct sk_buff *skb;	while ((skb = skb_dequeue(&shaper->sendq)) != NULL)		dev_kfree_skb(skb);	spin_lock_bh(&shaper->lock);	shaper_kick(shaper);	spin_unlock_bh(&shaper->lock);	del_timer_sync(&shaper->timer);	return 0;}/* *	Revectored calls. We alter the parameters and call the functions *	for our attached device. This enables us to bandwidth allocate after *	ARP and other resolutions and not before. */static int shaper_header(struct sk_buff *skb, struct net_device *dev,			 unsigned short type,			 const void *daddr, const void *saddr, unsigned len){	struct shaper *sh=dev->priv;	int v;	if(sh_debug)		printk("Shaper header\n");	skb->dev = sh->dev;	v = dev_hard_header(skb, sh->dev, type, daddr, saddr, len);	skb->dev = dev;	return v;}static int shaper_rebuild_header(struct sk_buff *skb){	struct shaper *sh=skb->dev->priv;	struct net_device *dev=skb->dev;	int v;	if(sh_debug)		printk("Shaper rebuild header\n");	skb->dev=sh->dev;	v = sh->dev->header_ops->rebuild(skb);	skb->dev=dev;	return v;}#if 0static int shaper_cache(struct neighbour *neigh, struct hh_cache *hh){	struct shaper *sh=neigh->dev->priv;	struct net_device *tmp;	int ret;	if(sh_debug)		printk("Shaper header cache bind\n");	tmp=neigh->dev;	neigh->dev=sh->dev;	ret=sh->hard_header_cache(neigh,hh);	neigh->dev=tmp;	return ret;}static void shaper_cache_update(struct hh_cache *hh, struct net_device *dev,	unsigned char *haddr){	struct shaper *sh=dev->priv;	if(sh_debug)		printk("Shaper cache update\n");	sh->header_cache_update(hh, sh->dev, haddr);}#endif#ifdef CONFIG_INETstatic int shaper_neigh_setup(struct neighbour *n){#ifdef CONFIG_INET	if (n->nud_state == NUD_NONE) {		n->ops = &arp_broken_ops;		n->output = n->ops->output;	}#endif	return 0;}static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p){#ifdef CONFIG_INET	if (p->tbl->family == AF_INET) {		p->neigh_setup = shaper_neigh_setup;		p->ucast_probes = 0;		p->mcast_probes = 0;	}#endif	return 0;}#else /* !(CONFIG_INET) */static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p){	return 0;}#endifstatic const struct header_ops shaper_ops = {	.create	 = shaper_header,	.rebuild = shaper_rebuild_header,};static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev){	sh->dev = dev;	sh->get_stats=dev->get_stats;	shdev->neigh_setup = shaper_neigh_setup_dev;	shdev->hard_header_len=dev->hard_header_len;	shdev->type=dev->type;	shdev->addr_len=dev->addr_len;	shdev->mtu=dev->mtu;	sh->bitspersec=0;	return 0;}static int shaper_ioctl(struct net_device *dev,  struct ifreq *ifr, int cmd){	struct shaperconf *ss= (struct shaperconf *)&ifr->ifr_ifru;	struct shaper *sh=dev->priv;	if(ss->ss_cmd == SHAPER_SET_DEV || ss->ss_cmd == SHAPER_SET_SPEED)	{		if(!capable(CAP_NET_ADMIN))			return -EPERM;	}	switch(ss->ss_cmd)	{		case SHAPER_SET_DEV:		{			struct net_device *them=__dev_get_by_name(&init_net, ss->ss_name);			if(them==NULL)				return -ENODEV;			if(sh->dev)				return -EBUSY;			return shaper_attach(dev,dev->priv, them);		}		case SHAPER_GET_DEV:			if(sh->dev==NULL)				return -ENODEV;			strcpy(ss->ss_name, sh->dev->name);			return 0;		case SHAPER_SET_SPEED:			shaper_setspeed(sh,ss->ss_speed);			return 0;		case SHAPER_GET_SPEED:			ss->ss_speed=sh->bitspersec;			return 0;		default:			return -EINVAL;	}}static void shaper_init_priv(struct net_device *dev){	struct shaper *sh = dev->priv;	skb_queue_head_init(&sh->sendq);	init_timer(&sh->timer);	sh->timer.function=shaper_timer;	sh->timer.data=(unsigned long)sh;	spin_lock_init(&sh->lock);}/* *	Add a shaper device to the system */static void __init shaper_setup(struct net_device *dev){	/*	 *	Set up the shaper.	 */	shaper_init_priv(dev);	dev->open		= shaper_open;	dev->stop		= shaper_close;	dev->hard_start_xmit 	= shaper_start_xmit;	dev->set_multicast_list = NULL;	/*	 *	Intialise the packet queues	 */	/*	 *	Handlers for when we attach to a device.	 */	dev->neigh_setup	= shaper_neigh_setup_dev;	dev->do_ioctl		= shaper_ioctl;	dev->hard_header_len	= 0;	dev->type		= ARPHRD_ETHER;	/* initially */	dev->set_mac_address	= NULL;	dev->mtu		= 1500;	dev->addr_len		= 0;	dev->tx_queue_len	= 10;	dev->flags		= 0;}static int shapers = 1;#ifdef MODULEmodule_param(shapers, int, 0);MODULE_PARM_DESC(shapers, "Traffic shaper: maximum number of shapers");#else /* MODULE */static int __init set_num_shapers(char *str){	shapers = simple_strtol(str, NULL, 0);	return 1;}__setup("shapers=", set_num_shapers);#endif /* MODULE */static struct net_device **devs;static unsigned int shapers_registered = 0;static int __init shaper_init(void){	int i;	size_t alloc_size;	struct net_device *dev;	char name[IFNAMSIZ];	if (shapers < 1)		return -ENODEV;	alloc_size = sizeof(*dev) * shapers;	devs = kzalloc(alloc_size, GFP_KERNEL);	if (!devs)		return -ENOMEM;	for (i = 0; i < shapers; i++) {		snprintf(name, IFNAMSIZ, "shaper%d", i);		dev = alloc_netdev(sizeof(struct shaper), name,				   shaper_setup);		if (!dev)			break;		if (register_netdev(dev)) {			free_netdev(dev);			break;		}		devs[i] = dev;		shapers_registered++;	}	if (!shapers_registered) {		kfree(devs);		devs = NULL;	}	return (shapers_registered ? 0 : -ENODEV);}static void __exit shaper_exit (void){	int i;	for (i = 0; i < shapers_registered; i++) {		if (devs[i]) {			unregister_netdev(devs[i]);			free_netdev(devs[i]);		}	}	kfree(devs);	devs = NULL;}module_init(shaper_init);module_exit(shaper_exit);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?