On Thu, Mar 18, 2004 at 12:58:58PM -0800, Stephen Hemminger wrote: [...] > A trivial example is: > tc qdisc add dev eth0 root delay latency 25ms rate 100mbit great to see Linux will begins to have a DummyNet replacement some weeks ago I had to use FreeBSD for this task btw, can this work in bridge mode? (like DummyNet) Ulisses > +++ 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@osdl.org> > + */ > + > +#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"); > - > : send the line "unsubscribe linux-net" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Debian GNU/Linux: a dream come true ----------------------------------------------------------------------------- "Computers are useless. They can only give answers." Pablo Picasso Humans are slow, innaccurate, and brilliant. Computers are fast, acurrate, and dumb. Together they are unbeatable ---> Visita http://www.valux.org/ para saber acerca de la <--- ---> Asociación Valenciana de Usuarios de Linux <--- - : send the line "unsubscribe linux-net" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html