blob: c5bd8064e6d81e68ff1a1b41ffca80ede3778fa1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/sched/sch_netem.c Network emulator
3 *
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.
8 *
9 * Many of the algorithms and ideas for this came from
10 * NIST Net which is not copyrighted.
11 *
12 * Authors: Stephen Hemminger <shemminger@osdl.org>
13 * Catalin(ux aka Dino) BOIE <catab at umbrella dot ro>
14 */
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/module.h>
17#include <linux/bitops.h>
18#include <linux/types.h>
19#include <linux/kernel.h>
20#include <linux/errno.h>
21#include <linux/netdevice.h>
22#include <linux/skbuff.h>
23#include <linux/rtnetlink.h>
24
25#include <net/pkt_sched.h>
26
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -080027#define VERSION "1.2"
Stephen Hemmingereb229c42005-11-03 13:49:01 -080028
Linus Torvalds1da177e2005-04-16 15:20:36 -070029/* Network Emulation Queuing algorithm.
30 ====================================
31
32 Sources: [1] Mark Carson, Darrin Santay, "NIST Net - A Linux-based
33 Network Emulation Tool
34 [2] Luigi Rizzo, DummyNet for FreeBSD
35
36 ----------------------------------------------------------------
37
38 This started out as a simple way to delay outgoing packets to
39 test TCP but has grown to include most of the functionality
40 of a full blown network emulator like NISTnet. It can delay
41 packets and add random jitter (and correlation). The random
42 distribution can be loaded from a table as well to provide
43 normal, Pareto, or experimental curves. Packet loss,
44 duplication, and reordering can also be emulated.
45
46 This qdisc does not do classification that can be handled in
47 layering other disciplines. It does not need to do bandwidth
48 control either since that can be handled by using token
49 bucket or other rate control.
50
51 The simulator is limited by the Linux timer resolution
52 and will create packet bursts on the HZ boundary (1ms).
53*/
54
55struct netem_sched_data {
56 struct Qdisc *qdisc;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 struct timer_list timer;
58
59 u32 latency;
60 u32 loss;
61 u32 limit;
62 u32 counter;
63 u32 gap;
64 u32 jitter;
65 u32 duplicate;
Stephen Hemminger0dca51d2005-05-26 12:55:48 -070066 u32 reorder;
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -080067 u32 corrupt;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69 struct crndstate {
70 unsigned long last;
71 unsigned long rho;
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -080072 } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74 struct disttable {
75 u32 size;
76 s16 table[0];
77 } *delay_dist;
78};
79
80/* Time stamp put into socket buffer control block */
81struct netem_skb_cb {
82 psched_time_t time_to_send;
83};
84
85/* init_crandom - initialize correlated random number generator
86 * Use entropy source for initial seed.
87 */
88static void init_crandom(struct crndstate *state, unsigned long rho)
89{
90 state->rho = rho;
91 state->last = net_random();
92}
93
94/* get_crandom - correlated random number generator
95 * Next number depends on last value.
96 * rho is scaled to avoid floating point.
97 */
98static unsigned long get_crandom(struct crndstate *state)
99{
100 u64 value, rho;
101 unsigned long answer;
102
103 if (state->rho == 0) /* no correllation */
104 return net_random();
105
106 value = net_random();
107 rho = (u64)state->rho + 1;
108 answer = (value * ((1ull<<32) - rho) + state->last * rho) >> 32;
109 state->last = answer;
110 return answer;
111}
112
113/* tabledist - return a pseudo-randomly distributed value with mean mu and
114 * std deviation sigma. Uses table lookup to approximate the desired
115 * distribution, and a uniformly-distributed pseudo-random source.
116 */
117static long tabledist(unsigned long mu, long sigma,
118 struct crndstate *state, const struct disttable *dist)
119{
120 long t, x;
121 unsigned long rnd;
122
123 if (sigma == 0)
124 return mu;
125
126 rnd = get_crandom(state);
127
128 /* default uniform distribution */
129 if (dist == NULL)
130 return (rnd % (2*sigma)) - sigma + mu;
131
132 t = dist->table[rnd % dist->size];
133 x = (sigma % NETEM_DIST_SCALE) * t;
134 if (x >= 0)
135 x += NETEM_DIST_SCALE/2;
136 else
137 x -= NETEM_DIST_SCALE/2;
138
139 return x / NETEM_DIST_SCALE + (sigma / NETEM_DIST_SCALE) * t + mu;
140}
141
Stephen Hemminger0afb51e2005-05-26 12:53:49 -0700142/*
143 * Insert one skb into qdisc.
144 * Note: parent depends on return value to account for queue length.
145 * NET_XMIT_DROP: queue length didn't change.
146 * NET_XMIT_SUCCESS: one skb was queued.
147 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
149{
150 struct netem_sched_data *q = qdisc_priv(sch);
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700151 struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb;
Stephen Hemminger0afb51e2005-05-26 12:53:49 -0700152 struct sk_buff *skb2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 int ret;
Stephen Hemminger0afb51e2005-05-26 12:53:49 -0700154 int count = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
Stephen Hemminger771018e2005-05-03 16:24:32 -0700156 pr_debug("netem_enqueue skb=%p\n", skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
Stephen Hemminger0afb51e2005-05-26 12:53:49 -0700158 /* Random duplication */
159 if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
160 ++count;
161
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 /* Random packet drop 0 => none, ~0 => all */
Stephen Hemminger0afb51e2005-05-26 12:53:49 -0700163 if (q->loss && q->loss >= get_crandom(&q->loss_cor))
164 --count;
165
166 if (count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 sch->qstats.drops++;
168 kfree_skb(skb);
Stephen Hemminger89bbb0a32006-04-28 12:11:36 -0700169 return NET_XMIT_BYPASS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 }
171
Stephen Hemminger0afb51e2005-05-26 12:53:49 -0700172 /*
173 * If we need to duplicate packet, then re-insert at top of the
174 * qdisc tree, since parent queuer expects that only one
175 * skb will be queued.
176 */
177 if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
178 struct Qdisc *rootq = sch->dev->qdisc;
179 u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
180 q->duplicate = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
Stephen Hemminger0afb51e2005-05-26 12:53:49 -0700182 rootq->enqueue(skb2, rootq);
183 q->duplicate = dupsave;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 }
185
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -0800186 /*
187 * Randomized packet corruption.
188 * Make copy if needed since we are modifying
189 * If packet is going to be hardware checksummed, then
190 * do it now in software before we mangle it.
191 */
192 if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
193 if (!(skb = skb_unshare(skb, GFP_ATOMIC))
194 || (skb->ip_summed == CHECKSUM_HW
195 && skb_checksum_help(skb, 0))) {
196 sch->qstats.drops++;
197 return NET_XMIT_DROP;
198 }
199
200 skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8);
201 }
202
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700203 if (q->gap == 0 /* not doing reordering */
204 || q->counter < q->gap /* inside last reordering gap */
205 || q->reorder < get_crandom(&q->reorder_cor)) {
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700206 psched_time_t now;
Stephen Hemminger07aaa112005-11-03 13:43:07 -0800207 psched_tdiff_t delay;
208
209 delay = tabledist(q->latency, q->jitter,
210 &q->delay_cor, q->delay_dist);
211
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700212 PSCHED_GET_TIME(now);
Stephen Hemminger07aaa112005-11-03 13:43:07 -0800213 PSCHED_TADD2(now, delay, cb->time_to_send);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 ++q->counter;
215 ret = q->qdisc->enqueue(skb, q->qdisc);
216 } else {
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700217 /*
218 * Do re-ordering by putting one out of N packets at the front
219 * of the queue.
220 */
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700221 PSCHED_GET_TIME(cb->time_to_send);
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700222 q->counter = 0;
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700223 ret = q->qdisc->ops->requeue(skb, q->qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 }
225
226 if (likely(ret == NET_XMIT_SUCCESS)) {
227 sch->q.qlen++;
228 sch->bstats.bytes += skb->len;
229 sch->bstats.packets++;
230 } else
231 sch->qstats.drops++;
232
Stephen Hemmingerd5d75cd2005-05-03 16:24:57 -0700233 pr_debug("netem: enqueue ret %d\n", ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 return ret;
235}
236
237/* Requeue packets but don't change time stamp */
238static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch)
239{
240 struct netem_sched_data *q = qdisc_priv(sch);
241 int ret;
242
243 if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
244 sch->q.qlen++;
245 sch->qstats.requeues++;
246 }
247
248 return ret;
249}
250
251static unsigned int netem_drop(struct Qdisc* sch)
252{
253 struct netem_sched_data *q = qdisc_priv(sch);
Patrick McHardy6d037a22006-03-20 19:00:49 -0800254 unsigned int len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Patrick McHardy6d037a22006-03-20 19:00:49 -0800256 if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 sch->q.qlen--;
258 sch->qstats.drops++;
259 }
260 return len;
261}
262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263static struct sk_buff *netem_dequeue(struct Qdisc *sch)
264{
265 struct netem_sched_data *q = qdisc_priv(sch);
266 struct sk_buff *skb;
267
268 skb = q->qdisc->dequeue(q->qdisc);
Stephen Hemminger771018e2005-05-03 16:24:32 -0700269 if (skb) {
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700270 const struct netem_skb_cb *cb
271 = (const struct netem_skb_cb *)skb->cb;
272 psched_time_t now;
Stephen Hemminger771018e2005-05-03 16:24:32 -0700273
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700274 /* if more time remaining? */
275 PSCHED_GET_TIME(now);
Stephen Hemminger07aaa112005-11-03 13:43:07 -0800276
277 if (PSCHED_TLESS(cb->time_to_send, now)) {
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700278 pr_debug("netem_dequeue: return skb=%p\n", skb);
279 sch->q.qlen--;
280 sch->flags &= ~TCQ_F_THROTTLED;
281 return skb;
Stephen Hemminger07aaa112005-11-03 13:43:07 -0800282 } else {
283 psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now);
284
285 if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
286 sch->qstats.drops++;
287
288 /* After this qlen is confused */
289 printk(KERN_ERR "netem: queue discpline %s could not requeue\n",
290 q->qdisc->ops->id);
291
292 sch->q.qlen--;
293 }
294
295 mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay));
296 sch->flags |= TCQ_F_THROTTLED;
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700297 }
Stephen Hemminger0f9f32a2005-05-26 12:55:01 -0700298 }
299
300 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301}
302
303static void netem_watchdog(unsigned long arg)
304{
305 struct Qdisc *sch = (struct Qdisc *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
Stephen Hemminger771018e2005-05-03 16:24:32 -0700307 pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen);
308 sch->flags &= ~TCQ_F_THROTTLED;
309 netif_schedule(sch->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
312static void netem_reset(struct Qdisc *sch)
313{
314 struct netem_sched_data *q = qdisc_priv(sch);
315
316 qdisc_reset(q->qdisc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 sch->q.qlen = 0;
Stephen Hemminger771018e2005-05-03 16:24:32 -0700318 sch->flags &= ~TCQ_F_THROTTLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 del_timer_sync(&q->timer);
320}
321
Stephen Hemminger300ce172005-10-30 13:47:34 -0800322/* Pass size change message down to embedded FIFO */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323static int set_fifo_limit(struct Qdisc *q, int limit)
324{
325 struct rtattr *rta;
326 int ret = -ENOMEM;
327
Stephen Hemminger300ce172005-10-30 13:47:34 -0800328 /* Hack to avoid sending change message to non-FIFO */
329 if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
330 return 0;
331
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
333 if (rta) {
334 rta->rta_type = RTM_NEWQDISC;
335 rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt));
336 ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
337
338 ret = q->ops->change(q, rta);
339 kfree(rta);
340 }
341 return ret;
342}
343
344/*
345 * Distribution data is a variable size payload containing
346 * signed 16 bit values.
347 */
348static int get_dist_table(struct Qdisc *sch, const struct rtattr *attr)
349{
350 struct netem_sched_data *q = qdisc_priv(sch);
351 unsigned long n = RTA_PAYLOAD(attr)/sizeof(__s16);
352 const __s16 *data = RTA_DATA(attr);
353 struct disttable *d;
354 int i;
355
356 if (n > 65536)
357 return -EINVAL;
358
359 d = kmalloc(sizeof(*d) + n*sizeof(d->table[0]), GFP_KERNEL);
360 if (!d)
361 return -ENOMEM;
362
363 d->size = n;
364 for (i = 0; i < n; i++)
365 d->table[i] = data[i];
366
367 spin_lock_bh(&sch->dev->queue_lock);
368 d = xchg(&q->delay_dist, d);
369 spin_unlock_bh(&sch->dev->queue_lock);
370
371 kfree(d);
372 return 0;
373}
374
375static int get_correlation(struct Qdisc *sch, const struct rtattr *attr)
376{
377 struct netem_sched_data *q = qdisc_priv(sch);
378 const struct tc_netem_corr *c = RTA_DATA(attr);
379
380 if (RTA_PAYLOAD(attr) != sizeof(*c))
381 return -EINVAL;
382
383 init_crandom(&q->delay_cor, c->delay_corr);
384 init_crandom(&q->loss_cor, c->loss_corr);
385 init_crandom(&q->dup_cor, c->dup_corr);
386 return 0;
387}
388
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700389static int get_reorder(struct Qdisc *sch, const struct rtattr *attr)
390{
391 struct netem_sched_data *q = qdisc_priv(sch);
392 const struct tc_netem_reorder *r = RTA_DATA(attr);
393
394 if (RTA_PAYLOAD(attr) != sizeof(*r))
395 return -EINVAL;
396
397 q->reorder = r->probability;
398 init_crandom(&q->reorder_cor, r->correlation);
399 return 0;
400}
401
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -0800402static int get_corrupt(struct Qdisc *sch, const struct rtattr *attr)
403{
404 struct netem_sched_data *q = qdisc_priv(sch);
405 const struct tc_netem_corrupt *r = RTA_DATA(attr);
406
407 if (RTA_PAYLOAD(attr) != sizeof(*r))
408 return -EINVAL;
409
410 q->corrupt = r->probability;
411 init_crandom(&q->corrupt_cor, r->correlation);
412 return 0;
413}
414
415/* Parse netlink message to set options */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416static int netem_change(struct Qdisc *sch, struct rtattr *opt)
417{
418 struct netem_sched_data *q = qdisc_priv(sch);
419 struct tc_netem_qopt *qopt;
420 int ret;
421
422 if (opt == NULL || RTA_PAYLOAD(opt) < sizeof(*qopt))
423 return -EINVAL;
424
425 qopt = RTA_DATA(opt);
426 ret = set_fifo_limit(q->qdisc, qopt->limit);
427 if (ret) {
428 pr_debug("netem: can't set fifo limit\n");
429 return ret;
430 }
431
432 q->latency = qopt->latency;
433 q->jitter = qopt->jitter;
434 q->limit = qopt->limit;
435 q->gap = qopt->gap;
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700436 q->counter = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 q->loss = qopt->loss;
438 q->duplicate = qopt->duplicate;
439
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700440 /* for compatiablity with earlier versions.
441 * if gap is set, need to assume 100% probablity
442 */
443 q->reorder = ~0;
444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 /* Handle nested options after initial queue options.
446 * Should have put all options in nested format but too late now.
447 */
448 if (RTA_PAYLOAD(opt) > sizeof(*qopt)) {
449 struct rtattr *tb[TCA_NETEM_MAX];
450 if (rtattr_parse(tb, TCA_NETEM_MAX,
451 RTA_DATA(opt) + sizeof(*qopt),
452 RTA_PAYLOAD(opt) - sizeof(*qopt)))
453 return -EINVAL;
454
455 if (tb[TCA_NETEM_CORR-1]) {
456 ret = get_correlation(sch, tb[TCA_NETEM_CORR-1]);
457 if (ret)
458 return ret;
459 }
460
461 if (tb[TCA_NETEM_DELAY_DIST-1]) {
462 ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST-1]);
463 if (ret)
464 return ret;
465 }
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -0800466
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700467 if (tb[TCA_NETEM_REORDER-1]) {
468 ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]);
469 if (ret)
470 return ret;
471 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -0800473 if (tb[TCA_NETEM_CORRUPT-1]) {
474 ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]);
475 if (ret)
476 return ret;
477 }
478 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480 return 0;
481}
482
Stephen Hemminger300ce172005-10-30 13:47:34 -0800483/*
484 * Special case version of FIFO queue for use by netem.
485 * It queues in order based on timestamps in skb's
486 */
487struct fifo_sched_data {
488 u32 limit;
489};
490
491static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
492{
493 struct fifo_sched_data *q = qdisc_priv(sch);
494 struct sk_buff_head *list = &sch->q;
495 const struct netem_skb_cb *ncb
496 = (const struct netem_skb_cb *)nskb->cb;
497 struct sk_buff *skb;
498
499 if (likely(skb_queue_len(list) < q->limit)) {
500 skb_queue_reverse_walk(list, skb) {
501 const struct netem_skb_cb *cb
502 = (const struct netem_skb_cb *)skb->cb;
503
Andrea Bittauaa875162005-11-20 13:41:05 -0800504 if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send))
Stephen Hemminger300ce172005-10-30 13:47:34 -0800505 break;
506 }
507
508 __skb_queue_after(list, skb, nskb);
509
510 sch->qstats.backlog += nskb->len;
511 sch->bstats.bytes += nskb->len;
512 sch->bstats.packets++;
513
514 return NET_XMIT_SUCCESS;
515 }
516
517 return qdisc_drop(nskb, sch);
518}
519
520static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
521{
522 struct fifo_sched_data *q = qdisc_priv(sch);
523
524 if (opt) {
525 struct tc_fifo_qopt *ctl = RTA_DATA(opt);
526 if (RTA_PAYLOAD(opt) < sizeof(*ctl))
527 return -EINVAL;
528
529 q->limit = ctl->limit;
530 } else
531 q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
532
533 return 0;
534}
535
536static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb)
537{
538 struct fifo_sched_data *q = qdisc_priv(sch);
539 struct tc_fifo_qopt opt = { .limit = q->limit };
540
541 RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
542 return skb->len;
543
544rtattr_failure:
545 return -1;
546}
547
548static struct Qdisc_ops tfifo_qdisc_ops = {
549 .id = "tfifo",
550 .priv_size = sizeof(struct fifo_sched_data),
551 .enqueue = tfifo_enqueue,
552 .dequeue = qdisc_dequeue_head,
553 .requeue = qdisc_requeue,
554 .drop = qdisc_queue_drop,
555 .init = tfifo_init,
556 .reset = qdisc_reset_queue,
557 .change = tfifo_init,
558 .dump = tfifo_dump,
559};
560
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561static int netem_init(struct Qdisc *sch, struct rtattr *opt)
562{
563 struct netem_sched_data *q = qdisc_priv(sch);
564 int ret;
565
566 if (!opt)
567 return -EINVAL;
568
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 init_timer(&q->timer);
570 q->timer.function = netem_watchdog;
571 q->timer.data = (unsigned long) sch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Stephen Hemminger300ce172005-10-30 13:47:34 -0800573 q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 if (!q->qdisc) {
575 pr_debug("netem: qdisc create failed\n");
576 return -ENOMEM;
577 }
578
579 ret = netem_change(sch, opt);
580 if (ret) {
581 pr_debug("netem: change failed\n");
582 qdisc_destroy(q->qdisc);
583 }
584 return ret;
585}
586
587static void netem_destroy(struct Qdisc *sch)
588{
589 struct netem_sched_data *q = qdisc_priv(sch);
590
591 del_timer_sync(&q->timer);
592 qdisc_destroy(q->qdisc);
593 kfree(q->delay_dist);
594}
595
596static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
597{
598 const struct netem_sched_data *q = qdisc_priv(sch);
599 unsigned char *b = skb->tail;
600 struct rtattr *rta = (struct rtattr *) b;
601 struct tc_netem_qopt qopt;
602 struct tc_netem_corr cor;
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700603 struct tc_netem_reorder reorder;
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -0800604 struct tc_netem_corrupt corrupt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
606 qopt.latency = q->latency;
607 qopt.jitter = q->jitter;
608 qopt.limit = q->limit;
609 qopt.loss = q->loss;
610 qopt.gap = q->gap;
611 qopt.duplicate = q->duplicate;
612 RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
613
614 cor.delay_corr = q->delay_cor.rho;
615 cor.loss_corr = q->loss_cor.rho;
616 cor.dup_corr = q->dup_cor.rho;
617 RTA_PUT(skb, TCA_NETEM_CORR, sizeof(cor), &cor);
Stephen Hemminger0dca51d2005-05-26 12:55:48 -0700618
619 reorder.probability = q->reorder;
620 reorder.correlation = q->reorder_cor.rho;
621 RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
622
Stephen Hemmingerc865e5d2005-12-21 19:03:44 -0800623 corrupt.probability = q->corrupt;
624 corrupt.correlation = q->corrupt_cor.rho;
625 RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
626
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 rta->rta_len = skb->tail - b;
628
629 return skb->len;
630
631rtattr_failure:
632 skb_trim(skb, b - skb->data);
633 return -1;
634}
635
636static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
637 struct sk_buff *skb, struct tcmsg *tcm)
638{
639 struct netem_sched_data *q = qdisc_priv(sch);
640
641 if (cl != 1) /* only one class */
642 return -ENOENT;
643
644 tcm->tcm_handle |= TC_H_MIN(1);
645 tcm->tcm_info = q->qdisc->handle;
646
647 return 0;
648}
649
650static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
651 struct Qdisc **old)
652{
653 struct netem_sched_data *q = qdisc_priv(sch);
654
655 if (new == NULL)
656 new = &noop_qdisc;
657
658 sch_tree_lock(sch);
659 *old = xchg(&q->qdisc, new);
660 qdisc_reset(*old);
661 sch->q.qlen = 0;
662 sch_tree_unlock(sch);
663
664 return 0;
665}
666
667static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg)
668{
669 struct netem_sched_data *q = qdisc_priv(sch);
670 return q->qdisc;
671}
672
673static unsigned long netem_get(struct Qdisc *sch, u32 classid)
674{
675 return 1;
676}
677
678static void netem_put(struct Qdisc *sch, unsigned long arg)
679{
680}
681
682static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
683 struct rtattr **tca, unsigned long *arg)
684{
685 return -ENOSYS;
686}
687
688static int netem_delete(struct Qdisc *sch, unsigned long arg)
689{
690 return -ENOSYS;
691}
692
693static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker)
694{
695 if (!walker->stop) {
696 if (walker->count >= walker->skip)
697 if (walker->fn(sch, 1, walker) < 0) {
698 walker->stop = 1;
699 return;
700 }
701 walker->count++;
702 }
703}
704
705static struct tcf_proto **netem_find_tcf(struct Qdisc *sch, unsigned long cl)
706{
707 return NULL;
708}
709
710static struct Qdisc_class_ops netem_class_ops = {
711 .graft = netem_graft,
712 .leaf = netem_leaf,
713 .get = netem_get,
714 .put = netem_put,
715 .change = netem_change_class,
716 .delete = netem_delete,
717 .walk = netem_walk,
718 .tcf_chain = netem_find_tcf,
719 .dump = netem_dump_class,
720};
721
722static struct Qdisc_ops netem_qdisc_ops = {
723 .id = "netem",
724 .cl_ops = &netem_class_ops,
725 .priv_size = sizeof(struct netem_sched_data),
726 .enqueue = netem_enqueue,
727 .dequeue = netem_dequeue,
728 .requeue = netem_requeue,
729 .drop = netem_drop,
730 .init = netem_init,
731 .reset = netem_reset,
732 .destroy = netem_destroy,
733 .change = netem_change,
734 .dump = netem_dump,
735 .owner = THIS_MODULE,
736};
737
738
739static int __init netem_module_init(void)
740{
Stephen Hemmingereb229c42005-11-03 13:49:01 -0800741 pr_info("netem: version " VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 return register_qdisc(&netem_qdisc_ops);
743}
744static void __exit netem_module_exit(void)
745{
746 unregister_qdisc(&netem_qdisc_ops);
747}
748module_init(netem_module_init)
749module_exit(netem_module_exit)
750MODULE_LICENSE("GPL");