[NET_SCHED]: Set parent classid in default qdiscs
[deliverable/linux.git] / net / sched / sch_netem.c
CommitLineData
1da177e4
LT
1/*
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
798b6b19 7 * 2 of the License.
1da177e4
LT
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
1da177e4
LT
16#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
c865e5d9 27#define VERSION "1.2"
eb229c4c 28
1da177e4
LT
29/* 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;
1da177e4
LT
57 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;
0dca51d3 66 u32 reorder;
c865e5d9 67 u32 corrupt;
1da177e4
LT
68
69 struct crndstate {
70 unsigned long last;
71 unsigned long rho;
c865e5d9 72 } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
1da177e4
LT
73
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
0afb51e7
SH
142/*
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 */
1da177e4
LT
148static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
149{
150 struct netem_sched_data *q = qdisc_priv(sch);
89e1df74
GC
151 /* We don't fill cb now as skb_unshare() may invalidate it */
152 struct netem_skb_cb *cb;
0afb51e7 153 struct sk_buff *skb2;
1da177e4 154 int ret;
0afb51e7 155 int count = 1;
1da177e4 156
771018e7 157 pr_debug("netem_enqueue skb=%p\n", skb);
1da177e4 158
0afb51e7
SH
159 /* Random duplication */
160 if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
161 ++count;
162
1da177e4 163 /* Random packet drop 0 => none, ~0 => all */
0afb51e7
SH
164 if (q->loss && q->loss >= get_crandom(&q->loss_cor))
165 --count;
166
167 if (count == 0) {
1da177e4
LT
168 sch->qstats.drops++;
169 kfree_skb(skb);
89bbb0a3 170 return NET_XMIT_BYPASS;
1da177e4
LT
171 }
172
4e8a5201
DM
173 skb_orphan(skb);
174
0afb51e7
SH
175 /*
176 * If we need to duplicate packet, then re-insert at top of the
177 * qdisc tree, since parent queuer expects that only one
178 * skb will be queued.
179 */
180 if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
181 struct Qdisc *rootq = sch->dev->qdisc;
182 u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
183 q->duplicate = 0;
184
185 rootq->enqueue(skb2, rootq);
186 q->duplicate = dupsave;
1da177e4
LT
187 }
188
c865e5d9
SH
189 /*
190 * Randomized packet corruption.
191 * Make copy if needed since we are modifying
192 * If packet is going to be hardware checksummed, then
193 * do it now in software before we mangle it.
194 */
195 if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
196 if (!(skb = skb_unshare(skb, GFP_ATOMIC))
84fa7933
PM
197 || (skb->ip_summed == CHECKSUM_PARTIAL
198 && skb_checksum_help(skb))) {
c865e5d9
SH
199 sch->qstats.drops++;
200 return NET_XMIT_DROP;
201 }
202
203 skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8);
204 }
205
89e1df74 206 cb = (struct netem_skb_cb *)skb->cb;
0dca51d3
SH
207 if (q->gap == 0 /* not doing reordering */
208 || q->counter < q->gap /* inside last reordering gap */
209 || q->reorder < get_crandom(&q->reorder_cor)) {
0f9f32ac 210 psched_time_t now;
07aaa115
SH
211 psched_tdiff_t delay;
212
213 delay = tabledist(q->latency, q->jitter,
214 &q->delay_cor, q->delay_dist);
215
0f9f32ac 216 PSCHED_GET_TIME(now);
07aaa115 217 PSCHED_TADD2(now, delay, cb->time_to_send);
1da177e4
LT
218 ++q->counter;
219 ret = q->qdisc->enqueue(skb, q->qdisc);
220 } else {
0dca51d3
SH
221 /*
222 * Do re-ordering by putting one out of N packets at the front
223 * of the queue.
224 */
0f9f32ac 225 PSCHED_GET_TIME(cb->time_to_send);
0dca51d3 226 q->counter = 0;
0f9f32ac 227 ret = q->qdisc->ops->requeue(skb, q->qdisc);
1da177e4
LT
228 }
229
230 if (likely(ret == NET_XMIT_SUCCESS)) {
231 sch->q.qlen++;
232 sch->bstats.bytes += skb->len;
233 sch->bstats.packets++;
234 } else
235 sch->qstats.drops++;
236
d5d75cd6 237 pr_debug("netem: enqueue ret %d\n", ret);
1da177e4
LT
238 return ret;
239}
240
241/* Requeue packets but don't change time stamp */
242static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch)
243{
244 struct netem_sched_data *q = qdisc_priv(sch);
245 int ret;
246
247 if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
248 sch->q.qlen++;
249 sch->qstats.requeues++;
250 }
251
252 return ret;
253}
254
255static unsigned int netem_drop(struct Qdisc* sch)
256{
257 struct netem_sched_data *q = qdisc_priv(sch);
6d037a26 258 unsigned int len = 0;
1da177e4 259
6d037a26 260 if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
1da177e4
LT
261 sch->q.qlen--;
262 sch->qstats.drops++;
263 }
264 return len;
265}
266
1da177e4
LT
267static struct sk_buff *netem_dequeue(struct Qdisc *sch)
268{
269 struct netem_sched_data *q = qdisc_priv(sch);
270 struct sk_buff *skb;
271
272 skb = q->qdisc->dequeue(q->qdisc);
771018e7 273 if (skb) {
0f9f32ac
SH
274 const struct netem_skb_cb *cb
275 = (const struct netem_skb_cb *)skb->cb;
276 psched_time_t now;
0f9f32ac
SH
277
278 /* if more time remaining? */
279 PSCHED_GET_TIME(now);
07aaa115
SH
280
281 if (PSCHED_TLESS(cb->time_to_send, now)) {
0f9f32ac
SH
282 pr_debug("netem_dequeue: return skb=%p\n", skb);
283 sch->q.qlen--;
284 sch->flags &= ~TCQ_F_THROTTLED;
285 return skb;
07aaa115
SH
286 } else {
287 psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now);
0f9f32ac 288
07aaa115
SH
289 if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
290 sch->qstats.drops++;
771018e7 291
07aaa115
SH
292 /* After this qlen is confused */
293 printk(KERN_ERR "netem: queue discpline %s could not requeue\n",
294 q->qdisc->ops->id);
295
296 sch->q.qlen--;
297 }
298
299 mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay));
300 sch->flags |= TCQ_F_THROTTLED;
301 }
0f9f32ac
SH
302 }
303
304 return NULL;
1da177e4
LT
305}
306
307static void netem_watchdog(unsigned long arg)
308{
309 struct Qdisc *sch = (struct Qdisc *)arg;
1da177e4 310
771018e7
SH
311 pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen);
312 sch->flags &= ~TCQ_F_THROTTLED;
313 netif_schedule(sch->dev);
1da177e4
LT
314}
315
316static void netem_reset(struct Qdisc *sch)
317{
318 struct netem_sched_data *q = qdisc_priv(sch);
319
320 qdisc_reset(q->qdisc);
1da177e4 321 sch->q.qlen = 0;
771018e7 322 sch->flags &= ~TCQ_F_THROTTLED;
1da177e4
LT
323 del_timer_sync(&q->timer);
324}
325
300ce174 326/* Pass size change message down to embedded FIFO */
1da177e4
LT
327static int set_fifo_limit(struct Qdisc *q, int limit)
328{
329 struct rtattr *rta;
330 int ret = -ENOMEM;
331
300ce174
SH
332 /* Hack to avoid sending change message to non-FIFO */
333 if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
334 return 0;
335
1da177e4
LT
336 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
337 if (rta) {
338 rta->rta_type = RTM_NEWQDISC;
339 rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt));
340 ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
341
342 ret = q->ops->change(q, rta);
343 kfree(rta);
344 }
345 return ret;
346}
347
348/*
349 * Distribution data is a variable size payload containing
350 * signed 16 bit values.
351 */
352static int get_dist_table(struct Qdisc *sch, const struct rtattr *attr)
353{
354 struct netem_sched_data *q = qdisc_priv(sch);
355 unsigned long n = RTA_PAYLOAD(attr)/sizeof(__s16);
356 const __s16 *data = RTA_DATA(attr);
357 struct disttable *d;
358 int i;
359
360 if (n > 65536)
361 return -EINVAL;
362
363 d = kmalloc(sizeof(*d) + n*sizeof(d->table[0]), GFP_KERNEL);
364 if (!d)
365 return -ENOMEM;
366
367 d->size = n;
368 for (i = 0; i < n; i++)
369 d->table[i] = data[i];
370
371 spin_lock_bh(&sch->dev->queue_lock);
372 d = xchg(&q->delay_dist, d);
373 spin_unlock_bh(&sch->dev->queue_lock);
374
375 kfree(d);
376 return 0;
377}
378
379static int get_correlation(struct Qdisc *sch, const struct rtattr *attr)
380{
381 struct netem_sched_data *q = qdisc_priv(sch);
382 const struct tc_netem_corr *c = RTA_DATA(attr);
383
384 if (RTA_PAYLOAD(attr) != sizeof(*c))
385 return -EINVAL;
386
387 init_crandom(&q->delay_cor, c->delay_corr);
388 init_crandom(&q->loss_cor, c->loss_corr);
389 init_crandom(&q->dup_cor, c->dup_corr);
390 return 0;
391}
392
0dca51d3
SH
393static int get_reorder(struct Qdisc *sch, const struct rtattr *attr)
394{
395 struct netem_sched_data *q = qdisc_priv(sch);
396 const struct tc_netem_reorder *r = RTA_DATA(attr);
397
398 if (RTA_PAYLOAD(attr) != sizeof(*r))
399 return -EINVAL;
400
401 q->reorder = r->probability;
402 init_crandom(&q->reorder_cor, r->correlation);
403 return 0;
404}
405
c865e5d9
SH
406static int get_corrupt(struct Qdisc *sch, const struct rtattr *attr)
407{
408 struct netem_sched_data *q = qdisc_priv(sch);
409 const struct tc_netem_corrupt *r = RTA_DATA(attr);
410
411 if (RTA_PAYLOAD(attr) != sizeof(*r))
412 return -EINVAL;
413
414 q->corrupt = r->probability;
415 init_crandom(&q->corrupt_cor, r->correlation);
416 return 0;
417}
418
419/* Parse netlink message to set options */
1da177e4
LT
420static int netem_change(struct Qdisc *sch, struct rtattr *opt)
421{
422 struct netem_sched_data *q = qdisc_priv(sch);
423 struct tc_netem_qopt *qopt;
424 int ret;
425
426 if (opt == NULL || RTA_PAYLOAD(opt) < sizeof(*qopt))
427 return -EINVAL;
428
429 qopt = RTA_DATA(opt);
430 ret = set_fifo_limit(q->qdisc, qopt->limit);
431 if (ret) {
432 pr_debug("netem: can't set fifo limit\n");
433 return ret;
434 }
435
436 q->latency = qopt->latency;
437 q->jitter = qopt->jitter;
438 q->limit = qopt->limit;
439 q->gap = qopt->gap;
0dca51d3 440 q->counter = 0;
1da177e4
LT
441 q->loss = qopt->loss;
442 q->duplicate = qopt->duplicate;
443
0dca51d3
SH
444 /* for compatiablity with earlier versions.
445 * if gap is set, need to assume 100% probablity
446 */
447 q->reorder = ~0;
448
1da177e4
LT
449 /* Handle nested options after initial queue options.
450 * Should have put all options in nested format but too late now.
451 */
452 if (RTA_PAYLOAD(opt) > sizeof(*qopt)) {
453 struct rtattr *tb[TCA_NETEM_MAX];
454 if (rtattr_parse(tb, TCA_NETEM_MAX,
455 RTA_DATA(opt) + sizeof(*qopt),
456 RTA_PAYLOAD(opt) - sizeof(*qopt)))
457 return -EINVAL;
458
459 if (tb[TCA_NETEM_CORR-1]) {
460 ret = get_correlation(sch, tb[TCA_NETEM_CORR-1]);
461 if (ret)
462 return ret;
463 }
464
465 if (tb[TCA_NETEM_DELAY_DIST-1]) {
466 ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST-1]);
467 if (ret)
468 return ret;
469 }
c865e5d9 470
0dca51d3
SH
471 if (tb[TCA_NETEM_REORDER-1]) {
472 ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]);
473 if (ret)
474 return ret;
475 }
1da177e4 476
c865e5d9
SH
477 if (tb[TCA_NETEM_CORRUPT-1]) {
478 ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]);
479 if (ret)
480 return ret;
481 }
482 }
1da177e4
LT
483
484 return 0;
485}
486
300ce174
SH
487/*
488 * Special case version of FIFO queue for use by netem.
489 * It queues in order based on timestamps in skb's
490 */
491struct fifo_sched_data {
492 u32 limit;
493};
494
495static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
496{
497 struct fifo_sched_data *q = qdisc_priv(sch);
498 struct sk_buff_head *list = &sch->q;
499 const struct netem_skb_cb *ncb
500 = (const struct netem_skb_cb *)nskb->cb;
501 struct sk_buff *skb;
502
503 if (likely(skb_queue_len(list) < q->limit)) {
504 skb_queue_reverse_walk(list, skb) {
505 const struct netem_skb_cb *cb
506 = (const struct netem_skb_cb *)skb->cb;
507
aa875166 508 if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send))
300ce174
SH
509 break;
510 }
511
512 __skb_queue_after(list, skb, nskb);
513
514 sch->qstats.backlog += nskb->len;
515 sch->bstats.bytes += nskb->len;
516 sch->bstats.packets++;
517
518 return NET_XMIT_SUCCESS;
519 }
520
521 return qdisc_drop(nskb, sch);
522}
523
524static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
525{
526 struct fifo_sched_data *q = qdisc_priv(sch);
527
528 if (opt) {
529 struct tc_fifo_qopt *ctl = RTA_DATA(opt);
530 if (RTA_PAYLOAD(opt) < sizeof(*ctl))
531 return -EINVAL;
532
533 q->limit = ctl->limit;
534 } else
535 q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
536
537 return 0;
538}
539
540static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb)
541{
542 struct fifo_sched_data *q = qdisc_priv(sch);
543 struct tc_fifo_qopt opt = { .limit = q->limit };
544
545 RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
546 return skb->len;
547
548rtattr_failure:
549 return -1;
550}
551
552static struct Qdisc_ops tfifo_qdisc_ops = {
553 .id = "tfifo",
554 .priv_size = sizeof(struct fifo_sched_data),
555 .enqueue = tfifo_enqueue,
556 .dequeue = qdisc_dequeue_head,
557 .requeue = qdisc_requeue,
558 .drop = qdisc_queue_drop,
559 .init = tfifo_init,
560 .reset = qdisc_reset_queue,
561 .change = tfifo_init,
562 .dump = tfifo_dump,
563};
564
1da177e4
LT
565static int netem_init(struct Qdisc *sch, struct rtattr *opt)
566{
567 struct netem_sched_data *q = qdisc_priv(sch);
568 int ret;
569
570 if (!opt)
571 return -EINVAL;
572
1da177e4
LT
573 init_timer(&q->timer);
574 q->timer.function = netem_watchdog;
575 q->timer.data = (unsigned long) sch;
1da177e4 576
9f9afec4
PM
577 q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops,
578 TC_H_MAKE(sch->handle, 1));
1da177e4
LT
579 if (!q->qdisc) {
580 pr_debug("netem: qdisc create failed\n");
581 return -ENOMEM;
582 }
583
584 ret = netem_change(sch, opt);
585 if (ret) {
586 pr_debug("netem: change failed\n");
587 qdisc_destroy(q->qdisc);
588 }
589 return ret;
590}
591
592static void netem_destroy(struct Qdisc *sch)
593{
594 struct netem_sched_data *q = qdisc_priv(sch);
595
596 del_timer_sync(&q->timer);
597 qdisc_destroy(q->qdisc);
598 kfree(q->delay_dist);
599}
600
601static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
602{
603 const struct netem_sched_data *q = qdisc_priv(sch);
604 unsigned char *b = skb->tail;
605 struct rtattr *rta = (struct rtattr *) b;
606 struct tc_netem_qopt qopt;
607 struct tc_netem_corr cor;
0dca51d3 608 struct tc_netem_reorder reorder;
c865e5d9 609 struct tc_netem_corrupt corrupt;
1da177e4
LT
610
611 qopt.latency = q->latency;
612 qopt.jitter = q->jitter;
613 qopt.limit = q->limit;
614 qopt.loss = q->loss;
615 qopt.gap = q->gap;
616 qopt.duplicate = q->duplicate;
617 RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
618
619 cor.delay_corr = q->delay_cor.rho;
620 cor.loss_corr = q->loss_cor.rho;
621 cor.dup_corr = q->dup_cor.rho;
622 RTA_PUT(skb, TCA_NETEM_CORR, sizeof(cor), &cor);
0dca51d3
SH
623
624 reorder.probability = q->reorder;
625 reorder.correlation = q->reorder_cor.rho;
626 RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
627
c865e5d9
SH
628 corrupt.probability = q->corrupt;
629 corrupt.correlation = q->corrupt_cor.rho;
630 RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
631
1da177e4
LT
632 rta->rta_len = skb->tail - b;
633
634 return skb->len;
635
636rtattr_failure:
637 skb_trim(skb, b - skb->data);
638 return -1;
639}
640
641static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
642 struct sk_buff *skb, struct tcmsg *tcm)
643{
644 struct netem_sched_data *q = qdisc_priv(sch);
645
646 if (cl != 1) /* only one class */
647 return -ENOENT;
648
649 tcm->tcm_handle |= TC_H_MIN(1);
650 tcm->tcm_info = q->qdisc->handle;
651
652 return 0;
653}
654
655static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
656 struct Qdisc **old)
657{
658 struct netem_sched_data *q = qdisc_priv(sch);
659
660 if (new == NULL)
661 new = &noop_qdisc;
662
663 sch_tree_lock(sch);
664 *old = xchg(&q->qdisc, new);
665 qdisc_reset(*old);
666 sch->q.qlen = 0;
667 sch_tree_unlock(sch);
668
669 return 0;
670}
671
672static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg)
673{
674 struct netem_sched_data *q = qdisc_priv(sch);
675 return q->qdisc;
676}
677
678static unsigned long netem_get(struct Qdisc *sch, u32 classid)
679{
680 return 1;
681}
682
683static void netem_put(struct Qdisc *sch, unsigned long arg)
684{
685}
686
687static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
688 struct rtattr **tca, unsigned long *arg)
689{
690 return -ENOSYS;
691}
692
693static int netem_delete(struct Qdisc *sch, unsigned long arg)
694{
695 return -ENOSYS;
696}
697
698static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker)
699{
700 if (!walker->stop) {
701 if (walker->count >= walker->skip)
702 if (walker->fn(sch, 1, walker) < 0) {
703 walker->stop = 1;
704 return;
705 }
706 walker->count++;
707 }
708}
709
710static struct tcf_proto **netem_find_tcf(struct Qdisc *sch, unsigned long cl)
711{
712 return NULL;
713}
714
715static struct Qdisc_class_ops netem_class_ops = {
716 .graft = netem_graft,
717 .leaf = netem_leaf,
718 .get = netem_get,
719 .put = netem_put,
720 .change = netem_change_class,
721 .delete = netem_delete,
722 .walk = netem_walk,
723 .tcf_chain = netem_find_tcf,
724 .dump = netem_dump_class,
725};
726
727static struct Qdisc_ops netem_qdisc_ops = {
728 .id = "netem",
729 .cl_ops = &netem_class_ops,
730 .priv_size = sizeof(struct netem_sched_data),
731 .enqueue = netem_enqueue,
732 .dequeue = netem_dequeue,
733 .requeue = netem_requeue,
734 .drop = netem_drop,
735 .init = netem_init,
736 .reset = netem_reset,
737 .destroy = netem_destroy,
738 .change = netem_change,
739 .dump = netem_dump,
740 .owner = THIS_MODULE,
741};
742
743
744static int __init netem_module_init(void)
745{
eb229c4c 746 pr_info("netem: version " VERSION "\n");
1da177e4
LT
747 return register_qdisc(&netem_qdisc_ops);
748}
749static void __exit netem_module_exit(void)
750{
751 unregister_qdisc(&netem_qdisc_ops);
752}
753module_init(netem_module_init)
754module_exit(netem_module_exit)
755MODULE_LICENSE("GPL");
This page took 0.225776 seconds and 5 git commands to generate.