[PATCH] packet delay scheduler

Linux Advanced Routing and Traffic Control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Okay, here is a very simple QOS scheduler that delays packets for 2.6.
It is good for testing, and might be useful for people who want to put
some class of traffic into a "penalty box".

Alexey's tc command is really easy to extend to new disciplines.  There is
a version which knows about this scheduler at:
	http://developer.osdl.org/shemminger/tcp/iproute2-delay.tar.bz2
The only changes were to tc/Makefile and new file tc/q_delay.c

A trivial example is:
	 tc qdisc add dev eth0 root delay latency 25ms rate 100mbit

Try it, if others find it useful please consider adding it.

diff -Nru a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
--- a/include/linux/pkt_sched.h	Thu Mar 18 12:04:02 2004
+++ b/include/linux/pkt_sched.h	Thu Mar 18 12:04:02 2004
@@ -432,4 +432,10 @@
 
 #define TCA_ATM_MAX	TCA_ATM_STATE
 
+/* Delay section */
+struct tc_dly_qopt
+{
+	__u32	latency;
+	__u32   limit;
+};	
 #endif
diff -Nru a/net/sched/Kconfig b/net/sched/Kconfig
--- a/net/sched/Kconfig	Thu Mar 18 12:04:02 2004
+++ b/net/sched/Kconfig	Thu Mar 18 12:04:02 2004
@@ -164,6 +164,17 @@
 	  To compile this code as a module, choose M here: the
 	  module will be called sch_dsmark.
 
+config NET_SCH_DELAY
+	tristate "Delay simulator"
+	depends on NET_SCHED
+	help
+	  Say Y if you want to delay packets by a fixed amount of
+	  time. This is often useful to simulate network delay when
+	  testing applications or protocols.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called sch_delay.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_SCHED && NETFILTER
diff -Nru a/net/sched/Makefile b/net/sched/Makefile
--- a/net/sched/Makefile	Thu Mar 18 12:04:02 2004
+++ b/net/sched/Makefile	Thu Mar 18 12:04:02 2004
@@ -22,6 +22,7 @@
 obj-$(CONFIG_NET_SCH_TEQL)	+= sch_teql.o
 obj-$(CONFIG_NET_SCH_PRIO)	+= sch_prio.o
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
+obj-$(CONFIG_NET_SCH_DELAY)	+= sch_delay.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
diff -Nru a/net/sched/sch_delay.c b/net/sched/sch_delay.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/sched/sch_delay.c	Thu Mar 18 12:04:02 2004
@@ -0,0 +1,269 @@
+/*
+ * net/sched/sch_delay.c	Simple constant delay
+ *
+ * 		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.
+ *
+ * Authors:	Stephen Hemminger <shemminger@xxxxxxxx>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/notifier.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+
+/*	Network delay simulator
+	This scheduler adds a fixed delay to all packets.
+	Similar to NISTnet and BSD Dummynet.
+
+	It uses byte fifo underneath similar to TBF */
+struct dly_sched_data {
+	u32	latency;
+	u32	limit;
+	struct timer_list timer;
+	struct Qdisc *qdisc;
+};
+
+/* Time stamp put into socket buffer control block */
+struct dly_skb_cb {
+	psched_time_t	queuetime;
+};
+
+/* Enqueue packets with underlying discipline (fifo)
+ * but mark them with current time first.
+ */
+static int dly_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+	struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
+	int ret;
+
+	PSCHED_GET_TIME(cb->queuetime);
+
+	/* Queue to underlying scheduler */
+	ret = q->qdisc->enqueue(skb, q->qdisc);
+	if (ret)
+		sch->stats.drops++;
+	else {
+		sch->q.qlen++;
+		sch->stats.bytes += skb->len;
+		sch->stats.packets++;
+	}
+	return 0;
+}
+
+/* Requeue packets but don't change time stamp */
+static int dly_requeue(struct sk_buff *skb, struct Qdisc *sch)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+	int ret;
+
+	ret = q->qdisc->ops->requeue(skb, q->qdisc);
+	if (ret == 0)
+		sch->q.qlen++;
+	return ret;
+}
+
+static unsigned int dly_drop(struct Qdisc *sch)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+	unsigned int len;
+
+	len = q->qdisc->ops->drop(q->qdisc);
+	if (len) {
+		sch->q.qlen--;
+		sch->stats.drops++;
+	}
+	return len;
+}
+
+/* Dequeue packet.
+ * If packet needs to be held up, then stop the
+ * queue and set timer to wakeup later.
+ */
+static struct sk_buff *dly_dequeue(struct Qdisc *sch)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+	struct sk_buff *skb = q->qdisc->dequeue(q->qdisc);
+
+	if (skb) {
+		struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
+		psched_time_t now;
+		long diff;
+
+		PSCHED_GET_TIME(now);
+		diff = q->latency - PSCHED_TDIFF(now, cb->queuetime);
+
+		if (diff <= 0) {
+			sch->q.qlen--;
+			sch->flags &= ~TCQ_F_THROTTLED;
+			return skb;
+		}
+
+		if (!netif_queue_stopped(sch->dev)) {
+			long delay = PSCHED_US2JIFFIE(diff);
+			if (delay <= 0)
+				delay = 1;
+			mod_timer(&q->timer, jiffies+delay);
+		}
+
+		if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
+			sch->q.qlen--;
+			sch->stats.drops++;
+		}
+		sch->flags |= TCQ_F_THROTTLED;
+	}
+	return NULL;
+}
+
+static void dly_reset(struct Qdisc *sch)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+
+	qdisc_reset(q->qdisc);
+	sch->q.qlen = 0;
+	sch->flags &= ~TCQ_F_THROTTLED;
+	del_timer(&q->timer);
+}
+
+static void dly_timer(unsigned long arg)
+{
+	struct Qdisc *sch = (struct Qdisc *)arg;
+
+	sch->flags &= ~TCQ_F_THROTTLED;
+	netif_schedule(sch->dev);
+}
+
+/* Tell Fifo the new limit. */
+static int change_limit(struct Qdisc *q, u32 limit)
+{
+	struct rtattr *rta;
+	int ret;
+
+	rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
+	if (!rta)
+		return -ENOMEM;
+
+	rta->rta_type = RTM_NEWQDISC;
+	((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
+	ret = q->ops->change(q, rta);
+	kfree(rta);
+
+	return ret;
+}
+
+/* Setup underlying FIFO discipline */
+static int dly_change(struct Qdisc *sch, struct rtattr *opt)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+	struct tc_dly_qopt *qopt = RTA_DATA(opt);
+	int err;
+
+	if (q->qdisc == &noop_qdisc) {
+		struct Qdisc *child
+			= qdisc_create_dflt(sch->dev, &bfifo_qdisc_ops);
+		if (!child)
+			return -EINVAL;
+		q->qdisc = child;
+	}
+
+	err = change_limit(q->qdisc, qopt->limit);
+	if (err) {
+		qdisc_destroy(q->qdisc);
+		q->qdisc = &noop_qdisc;
+	} else {
+		q->latency = qopt->latency;
+		q->limit = qopt->limit;
+	}
+	return err;
+}
+
+static int dly_init(struct Qdisc *sch, struct rtattr *opt)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+
+	if (!opt)
+		return -EINVAL;
+
+	init_timer(&q->timer);
+	q->timer.function = dly_timer;
+	q->timer.data = (unsigned long) sch;
+	q->qdisc = &noop_qdisc;
+
+	return dly_change(sch, opt);
+}
+
+static void dly_destroy(struct Qdisc *sch)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+
+	del_timer(&q->timer);
+	qdisc_destroy(q->qdisc);
+	q->qdisc = &noop_qdisc;
+}
+
+static int dly_dump(struct Qdisc *sch, struct sk_buff *skb)
+{
+	struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
+	unsigned char	 *b = skb->tail;
+	struct tc_dly_qopt qopt;
+
+	qopt.latency = q->latency;
+	qopt.limit = q->limit;
+
+	RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
+
+	return skb->len;
+
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+static struct Qdisc_ops dly_qdisc_ops = {
+	.id		=	"delay",
+	.priv_size	=	sizeof(struct dly_sched_data),
+	.enqueue	=	dly_enqueue,
+	.dequeue	=	dly_dequeue,
+	.requeue	=	dly_requeue,
+	.drop		=	dly_drop,
+	.init		=	dly_init,
+	.reset		=	dly_reset,
+	.destroy	=	dly_destroy,
+	.change		=	dly_change,
+	.dump		=	dly_dump,
+	.owner		=	THIS_MODULE,
+};
+
+
+static int __init dly_module_init(void)
+{
+	return register_qdisc(&dly_qdisc_ops);
+}
+static void __exit dly_module_exit(void)
+{
+	unregister_qdisc(&dly_qdisc_ops);
+}
+module_init(dly_module_init)
+module_exit(dly_module_exit)
+MODULE_LICENSE("GPL");
_______________________________________________
LARTC mailing list / LARTC@xxxxxxxxxxxxxxx
http://mailman.ds9a.nl/mailman/listinfo/lartc HOWTO: http://lartc.org/

[Index of Archives]     [LARTC Home Page]     [Netfilter]     [Netfilter Development]     [Network Development]     [Bugtraq]     [GCC Help]     [Yosemite News]     [Linux Kernel]     [Fedora Users]
  Powered by Linux