Remove obsolete #include <linux/config.h>
[safe/jmp/linux-2.6] / net / sched / sch_netem.c
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
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
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
27 #define VERSION "1.2"
28
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
55 struct netem_sched_data {
56         struct Qdisc    *qdisc;
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;
66         u32 reorder;
67         u32 corrupt;
68
69         struct crndstate {
70                 unsigned long last;
71                 unsigned long rho;
72         } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
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 */
81 struct 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  */
88 static 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  */
98 static 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  */
117 static 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
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  */
148 static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
149 {
150         struct netem_sched_data *q = qdisc_priv(sch);
151         struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb;
152         struct sk_buff *skb2;
153         int ret;
154         int count = 1;
155
156         pr_debug("netem_enqueue skb=%p\n", skb);
157
158         /* Random duplication */
159         if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
160                 ++count;
161
162         /* Random packet drop 0 => none, ~0 => all */
163         if (q->loss && q->loss >= get_crandom(&q->loss_cor))
164                 --count;
165
166         if (count == 0) {
167                 sch->qstats.drops++;
168                 kfree_skb(skb);
169                 return NET_XMIT_BYPASS;
170         }
171
172         /*
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;
181
182                 rootq->enqueue(skb2, rootq);
183                 q->duplicate = dupsave;
184         }
185
186         /*
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
203         if (q->gap == 0                 /* not doing reordering */
204             || q->counter < q->gap      /* inside last reordering gap */
205             || q->reorder < get_crandom(&q->reorder_cor)) {
206                 psched_time_t now;
207                 psched_tdiff_t delay;
208
209                 delay = tabledist(q->latency, q->jitter,
210                                   &q->delay_cor, q->delay_dist);
211
212                 PSCHED_GET_TIME(now);
213                 PSCHED_TADD2(now, delay, cb->time_to_send);
214                 ++q->counter;
215                 ret = q->qdisc->enqueue(skb, q->qdisc);
216         } else {
217                 /* 
218                  * Do re-ordering by putting one out of N packets at the front
219                  * of the queue.
220                  */
221                 PSCHED_GET_TIME(cb->time_to_send);
222                 q->counter = 0;
223                 ret = q->qdisc->ops->requeue(skb, q->qdisc);
224         }
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
233         pr_debug("netem: enqueue ret %d\n", ret);
234         return ret;
235 }
236
237 /* Requeue packets but don't change time stamp */
238 static 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
251 static unsigned int netem_drop(struct Qdisc* sch)
252 {
253         struct netem_sched_data *q = qdisc_priv(sch);
254         unsigned int len = 0;
255
256         if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
257                 sch->q.qlen--;
258                 sch->qstats.drops++;
259         }
260         return len;
261 }
262
263 static 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);
269         if (skb) {
270                 const struct netem_skb_cb *cb
271                         = (const struct netem_skb_cb *)skb->cb;
272                 psched_time_t now;
273
274                 /* if more time remaining? */
275                 PSCHED_GET_TIME(now);
276
277                 if (PSCHED_TLESS(cb->time_to_send, now)) {
278                         pr_debug("netem_dequeue: return skb=%p\n", skb);
279                         sch->q.qlen--;
280                         sch->flags &= ~TCQ_F_THROTTLED;
281                         return skb;
282                 } 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;
297                 }
298         }
299
300         return NULL;
301 }
302
303 static void netem_watchdog(unsigned long arg)
304 {
305         struct Qdisc *sch = (struct Qdisc *)arg;
306
307         pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen);
308         sch->flags &= ~TCQ_F_THROTTLED;
309         netif_schedule(sch->dev);
310 }
311
312 static void netem_reset(struct Qdisc *sch)
313 {
314         struct netem_sched_data *q = qdisc_priv(sch);
315
316         qdisc_reset(q->qdisc);
317         sch->q.qlen = 0;
318         sch->flags &= ~TCQ_F_THROTTLED;
319         del_timer_sync(&q->timer);
320 }
321
322 /* Pass size change message down to embedded FIFO */
323 static int set_fifo_limit(struct Qdisc *q, int limit)
324 {
325         struct rtattr *rta;
326         int ret = -ENOMEM;
327
328         /* Hack to avoid sending change message to non-FIFO */
329         if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
330                 return 0;
331
332         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  */
348 static 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
375 static 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
389 static 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
402 static 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 */
416 static 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;
436         q->counter = 0;
437         q->loss = qopt->loss;
438         q->duplicate = qopt->duplicate;
439
440         /* for compatiablity with earlier versions.
441          * if gap is set, need to assume 100% probablity
442          */
443         q->reorder = ~0;
444
445         /* 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                 }
466
467                 if (tb[TCA_NETEM_REORDER-1]) {
468                         ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]);
469                         if (ret)
470                                 return ret;
471                 }
472
473                 if (tb[TCA_NETEM_CORRUPT-1]) {
474                         ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]);
475                         if (ret)
476                                 return ret;
477                 }
478         }
479
480         return 0;
481 }
482
483 /*
484  * Special case version of FIFO queue for use by netem.
485  * It queues in order based on timestamps in skb's
486  */
487 struct fifo_sched_data {
488         u32 limit;
489 };
490
491 static 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
504                         if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send))
505                                 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
520 static 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
536 static 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
544 rtattr_failure:
545         return -1;
546 }
547
548 static 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
561 static 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
569         init_timer(&q->timer);
570         q->timer.function = netem_watchdog;
571         q->timer.data = (unsigned long) sch;
572
573         q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops);
574         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
587 static 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
596 static 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;
603         struct tc_netem_reorder reorder;
604         struct tc_netem_corrupt corrupt;
605
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);
618
619         reorder.probability = q->reorder;
620         reorder.correlation = q->reorder_cor.rho;
621         RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
622
623         corrupt.probability = q->corrupt;
624         corrupt.correlation = q->corrupt_cor.rho;
625         RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
626
627         rta->rta_len = skb->tail - b;
628
629         return skb->len;
630
631 rtattr_failure:
632         skb_trim(skb, b - skb->data);
633         return -1;
634 }
635
636 static 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
650 static 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
667 static 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
673 static unsigned long netem_get(struct Qdisc *sch, u32 classid)
674 {
675         return 1;
676 }
677
678 static void netem_put(struct Qdisc *sch, unsigned long arg)
679 {
680 }
681
682 static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 
683                             struct rtattr **tca, unsigned long *arg)
684 {
685         return -ENOSYS;
686 }
687
688 static int netem_delete(struct Qdisc *sch, unsigned long arg)
689 {
690         return -ENOSYS;
691 }
692
693 static 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
705 static struct tcf_proto **netem_find_tcf(struct Qdisc *sch, unsigned long cl)
706 {
707         return NULL;
708 }
709
710 static 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
722 static 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
739 static int __init netem_module_init(void)
740 {
741         pr_info("netem: version " VERSION "\n");
742         return register_qdisc(&netem_qdisc_ops);
743 }
744 static void __exit netem_module_exit(void)
745 {
746         unregister_qdisc(&netem_qdisc_ops);
747 }
748 module_init(netem_module_init)
749 module_exit(netem_module_exit)
750 MODULE_LICENSE("GPL");