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