2 * net/sched/sch_delay.c Simple constant delay
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
9 * Authors: Stephen Hemminger <shemminger@osdl.org>
12 #include <linux/config.h>
13 #include <linux/module.h>
14 #include <linux/types.h>
15 #include <linux/kernel.h>
17 #include <linux/string.h>
19 #include <linux/socket.h>
20 #include <linux/sockios.h>
22 #include <linux/errno.h>
23 #include <linux/interrupt.h>
24 #include <linux/if_ether.h>
25 #include <linux/inet.h>
26 #include <linux/netdevice.h>
27 #include <linux/etherdevice.h>
28 #include <linux/notifier.h>
30 #include <net/route.h>
31 #include <linux/skbuff.h>
33 #include <net/pkt_sched.h>
35 /* Network delay simulator
36 This scheduler adds a fixed delay to all packets.
37 Similar to NISTnet and BSD Dummynet.
39 It uses byte fifo underneath similar to TBF */
40 struct dly_sched_data {
44 struct timer_list timer;
48 /* Time stamp put into socket buffer control block */
50 psched_time_t queuetime;
53 /* Enqueue packets with underlying discipline (fifo)
54 * but mark them with current time first.
56 static int dly_enqueue(struct sk_buff *skb, struct Qdisc *sch)
58 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
59 struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
62 /* Random packet drop 0 => none, ~0 => all */
63 if (q->loss >= net_random()) {
65 return 0; /* lie about loss so TCP doesn't know */
68 PSCHED_GET_TIME(cb->queuetime);
70 /* Queue to underlying scheduler */
71 ret = q->qdisc->enqueue(skb, q->qdisc);
76 sch->stats.bytes += skb->len;
82 /* Requeue packets but don't change time stamp */
83 static int dly_requeue(struct sk_buff *skb, struct Qdisc *sch)
85 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
88 ret = q->qdisc->ops->requeue(skb, q->qdisc);
94 static unsigned int dly_drop(struct Qdisc *sch)
96 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
99 len = q->qdisc->ops->drop(q->qdisc);
108 * If packet needs to be held up, then stop the
109 * queue and set timer to wakeup later.
111 static struct sk_buff *dly_dequeue(struct Qdisc *sch)
113 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
117 skb = q->qdisc->dequeue(q->qdisc);
119 struct dly_skb_cb *cb = (struct dly_skb_cb *)skb->cb;
123 PSCHED_GET_TIME(now);
124 diff = q->latency - PSCHED_TDIFF(now, cb->queuetime);
128 sch->flags &= ~TCQ_F_THROTTLED;
132 if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
138 delay = PSCHED_US2JIFFIE(diff);
141 mod_timer(&q->timer, jiffies+delay);
143 sch->flags |= TCQ_F_THROTTLED;
148 static void dly_reset(struct Qdisc *sch)
150 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
152 qdisc_reset(q->qdisc);
154 sch->flags &= ~TCQ_F_THROTTLED;
155 del_timer(&q->timer);
158 static void dly_timer(unsigned long arg)
160 struct Qdisc *sch = (struct Qdisc *)arg;
162 sch->flags &= ~TCQ_F_THROTTLED;
163 netif_schedule(sch->dev);
166 /* Tell Fifo the new limit. */
167 static int change_limit(struct Qdisc *q, u32 limit)
172 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
176 rta->rta_type = RTM_NEWQDISC;
177 rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt));
178 ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
179 ret = q->ops->change(q, rta);
185 /* Setup underlying FIFO discipline */
186 static int dly_change(struct Qdisc *sch, struct rtattr *opt)
188 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
189 struct tc_dly_qopt *qopt = RTA_DATA(opt);
192 if (q->qdisc == &noop_qdisc) {
194 = qdisc_create_dflt(sch->dev, &bfifo_qdisc_ops);
200 err = change_limit(q->qdisc, qopt->limit);
202 qdisc_destroy(q->qdisc);
203 q->qdisc = &noop_qdisc;
205 q->latency = qopt->latency;
206 q->limit = qopt->limit;
207 q->loss = qopt->loss;
212 static int dly_init(struct Qdisc *sch, struct rtattr *opt)
214 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
219 init_timer(&q->timer);
220 q->timer.function = dly_timer;
221 q->timer.data = (unsigned long) sch;
222 q->qdisc = &noop_qdisc;
224 return dly_change(sch, opt);
227 static void dly_destroy(struct Qdisc *sch)
229 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
231 del_timer(&q->timer);
232 qdisc_destroy(q->qdisc);
233 q->qdisc = &noop_qdisc;
236 static int dly_dump(struct Qdisc *sch, struct sk_buff *skb)
238 struct dly_sched_data *q = (struct dly_sched_data *)sch->data;
239 unsigned char *b = skb->tail;
240 struct tc_dly_qopt qopt;
242 qopt.latency = q->latency;
243 qopt.limit = q->limit;
246 RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
251 skb_trim(skb, b - skb->data);
255 static struct Qdisc_ops dly_qdisc_ops = {
257 .priv_size = sizeof(struct dly_sched_data),
258 .enqueue = dly_enqueue,
259 .dequeue = dly_dequeue,
260 .requeue = dly_requeue,
264 .destroy = dly_destroy,
265 .change = dly_change,
267 .owner = THIS_MODULE,
271 static int __init dly_module_init(void)
273 return register_qdisc(&dly_qdisc_ops);
275 static void __exit dly_module_exit(void)
277 unregister_qdisc(&dly_qdisc_ops);
279 module_init(dly_module_init)
280 module_exit(dly_module_exit)
281 MODULE_LICENSE("GPL");