Blame view

net/sched/sch_netem.c 27.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
  /*
   * net/sched/sch_netem.c	Network emulator
   *
   * 		This program is free software; you can redistribute it and/or
   * 		modify it under the terms of the GNU General Public License
   * 		as published by the Free Software Foundation; either version
798b6b19d   Stephen Hemminger   [PATCH] skge, sky...
7
   * 		2 of the License.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
   *
   *  		Many of the algorithms and ideas for this came from
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
10
   *		NIST Net which is not copyrighted.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
   *
   * Authors:	Stephen Hemminger <shemminger@osdl.org>
   *		Catalin(ux aka Dino) BOIE <catab at umbrella dot ro>
   */
b7f080cfe   Alexey Dobriyan   net: remove mm.h ...
15
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <linux/skbuff.h>
78776d3f2   David S. Miller   sch_netem: Need t...
22
  #include <linux/vmalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  #include <linux/rtnetlink.h>
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
24
  #include <linux/reciprocal_div.h>
aec0a40a6   Eric Dumazet   netem: use rb tre...
25
  #include <linux/rbtree.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
27
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
  #include <net/pkt_sched.h>
e4ae004b8   Eric Dumazet   netem: add ECN ca...
29
  #include <net/inet_ecn.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30

250a65f78   stephen hemminger   netem: update ver...
31
  #define VERSION "1.3"
eb229c4cd   Stephen Hemminger   [NETEM]: Add vers...
32

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  /*	Network Emulation Queuing algorithm.
  	====================================
  
  	Sources: [1] Mark Carson, Darrin Santay, "NIST Net - A Linux-based
  		 Network Emulation Tool
  		 [2] Luigi Rizzo, DummyNet for FreeBSD
  
  	 ----------------------------------------------------------------
  
  	 This started out as a simple way to delay outgoing packets to
  	 test TCP but has grown to include most of the functionality
  	 of a full blown network emulator like NISTnet. It can delay
  	 packets and add random jitter (and correlation). The random
  	 distribution can be loaded from a table as well to provide
  	 normal, Pareto, or experimental curves. Packet loss,
  	 duplication, and reordering can also be emulated.
  
  	 This qdisc does not do classification that can be handled in
  	 layering other disciplines.  It does not need to do bandwidth
  	 control either since that can be handled by using token
  	 bucket or other rate control.
661b79725   stephen hemminger   netem: revised co...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  
       Correlated Loss Generator models
  
  	Added generation of correlated loss according to the
  	"Gilbert-Elliot" model, a 4-state markov model.
  
  	References:
  	[1] NetemCLG Home http://netgroup.uniroma2.it/NetemCLG
  	[2] S. Salsano, F. Ludovici, A. Ordine, "Definition of a general
  	and intuitive loss model for packet networks and its implementation
  	in the Netem module in the Linux kernel", available in [1]
  
  	Authors: Stefano Salsano <stefano.salsano at uniroma2.it
  		 Fabio Ludovici <fabio.ludovici at yahoo.it>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
  */
  
  struct netem_sched_data {
aec0a40a6   Eric Dumazet   netem: use rb tre...
71
72
  	/* internal t(ime)fifo qdisc uses t_root and sch->limit */
  	struct rb_root t_root;
50612537e   Eric Dumazet   netem: fix classf...
73
74
  
  	/* optional qdisc for classful handling (NULL at netem init) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	struct Qdisc	*qdisc;
50612537e   Eric Dumazet   netem: fix classf...
76

59cb5c673   Patrick McHardy   [NET_SCHED]: sch_...
77
  	struct qdisc_watchdog watchdog;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78

b407621c3   Stephen Hemminger   [NETEM]: use bett...
79
80
  	psched_tdiff_t latency;
  	psched_tdiff_t jitter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  	u32 loss;
e4ae004b8   Eric Dumazet   netem: add ECN ca...
82
  	u32 ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
  	u32 limit;
  	u32 counter;
  	u32 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  	u32 duplicate;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
87
  	u32 reorder;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
88
  	u32 corrupt;
6a031f67c   Yang Yingliang   sch_netem: suppor...
89
  	u64 rate;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
90
91
  	s32 packet_overhead;
  	u32 cell_size;
809fa972f   Hannes Frederic Sowa   reciprocal_divide...
92
  	struct reciprocal_value cell_size_reciprocal;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
93
  	s32 cell_overhead;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
  
  	struct crndstate {
b407621c3   Stephen Hemminger   [NETEM]: use bett...
96
97
  		u32 last;
  		u32 rho;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
98
  	} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
103
  
  	struct disttable {
  		u32  size;
  		s16 table[0];
  	} *delay_dist;
661b79725   stephen hemminger   netem: revised co...
104
105
106
107
108
109
  
  	enum  {
  		CLG_RANDOM,
  		CLG_4_STATES,
  		CLG_GILB_ELL,
  	} loss_model;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
110
111
112
113
114
115
  	enum {
  		TX_IN_GAP_PERIOD = 1,
  		TX_IN_BURST_PERIOD,
  		LOST_IN_GAP_PERIOD,
  		LOST_IN_BURST_PERIOD,
  	} _4_state_model;
c045a734d   Yang Yingliang   sch_netem: replac...
116
117
118
119
  	enum {
  		GOOD_STATE = 1,
  		BAD_STATE,
  	} GE_state_model;
661b79725   stephen hemminger   netem: revised co...
120
121
122
123
124
125
126
127
128
129
130
131
  	/* Correlated Loss Generation models */
  	struct clgstate {
  		/* state of the Markov chain */
  		u8 state;
  
  		/* 4-states and Gilbert-Elliot models */
  		u32 a1;	/* p13 for 4-states or p for GE */
  		u32 a2;	/* p31 for 4-states or r for GE */
  		u32 a3;	/* p32 for 4-states or h for GE */
  		u32 a4;	/* p14 for 4-states or 1-k for GE */
  		u32 a5; /* p23 used only in 4-states */
  	} clg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  };
50612537e   Eric Dumazet   netem: fix classf...
133
134
  /* Time stamp put into socket buffer control block
   * Only valid when skbs are in our internal t(ime)fifo queue.
56b174256   Eric Dumazet   net: add rbnode t...
135
136
137
138
   *
   * As skb->rbnode uses same storage than skb->next, skb->prev and skb->tstamp,
   * and skb->next & skb->prev are scratch space for a qdisc,
   * we save skb->tstamp value in skb->cb[] before destroying it.
50612537e   Eric Dumazet   netem: fix classf...
139
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  struct netem_skb_cb {
  	psched_time_t	time_to_send;
aec0a40a6   Eric Dumazet   netem: use rb tre...
142
  	ktime_t		tstamp_save;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  };
5f86173bd   Jussi Kivilinna   net_sched: Add qd...
144
145
  static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb)
  {
aec0a40a6   Eric Dumazet   netem: use rb tre...
146
  	/* we assume we can use skb next/prev/tstamp as storage for rb_node */
16bda13d9   David S. Miller   net: Make qdisc_s...
147
  	qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb));
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
148
  	return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data;
5f86173bd   Jussi Kivilinna   net_sched: Add qd...
149
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
153
154
155
  /* init_crandom - initialize correlated random number generator
   * Use entropy source for initial seed.
   */
  static void init_crandom(struct crndstate *state, unsigned long rho)
  {
  	state->rho = rho;
63862b5be   Aruna-Hewapathirane   net: replace macr...
156
  	state->last = prandom_u32();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
  }
  
  /* get_crandom - correlated random number generator
   * Next number depends on last value.
   * rho is scaled to avoid floating point.
   */
b407621c3   Stephen Hemminger   [NETEM]: use bett...
163
  static u32 get_crandom(struct crndstate *state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
  {
  	u64 value, rho;
  	unsigned long answer;
bb2f8cc0e   Stephen Hemminger   [NETEM]: spelling...
167
  	if (state->rho == 0)	/* no correlation */
63862b5be   Aruna-Hewapathirane   net: replace macr...
168
  		return prandom_u32();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169

63862b5be   Aruna-Hewapathirane   net: replace macr...
170
  	value = prandom_u32();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
  	rho = (u64)state->rho + 1;
  	answer = (value * ((1ull<<32) - rho) + state->last * rho) >> 32;
  	state->last = answer;
  	return answer;
  }
661b79725   stephen hemminger   netem: revised co...
176
177
178
179
180
181
182
  /* loss_4state - 4-state model loss generator
   * Generates losses according to the 4-state Markov chain adopted in
   * the GI (General and Intuitive) loss model.
   */
  static bool loss_4state(struct netem_sched_data *q)
  {
  	struct clgstate *clg = &q->clg;
63862b5be   Aruna-Hewapathirane   net: replace macr...
183
  	u32 rnd = prandom_u32();
661b79725   stephen hemminger   netem: revised co...
184
185
  
  	/*
25985edce   Lucas De Marchi   Fix common misspe...
186
  	 * Makes a comparison between rnd and the transition
661b79725   stephen hemminger   netem: revised co...
187
188
189
  	 * probabilities outgoing from the current state, then decides the
  	 * next state and if the next packet has to be transmitted or lost.
  	 * The four states correspond to:
a6e2fe17e   Yang Yingliang   sch_netem: replac...
190
191
192
193
  	 *   TX_IN_GAP_PERIOD => successfully transmitted packets within a gap period
  	 *   LOST_IN_BURST_PERIOD => isolated losses within a gap period
  	 *   LOST_IN_GAP_PERIOD => lost packets within a burst period
  	 *   TX_IN_GAP_PERIOD => successfully transmitted packets within a burst period
661b79725   stephen hemminger   netem: revised co...
194
195
  	 */
  	switch (clg->state) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
196
  	case TX_IN_GAP_PERIOD:
661b79725   stephen hemminger   netem: revised co...
197
  		if (rnd < clg->a4) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
198
  			clg->state = LOST_IN_BURST_PERIOD;
661b79725   stephen hemminger   netem: revised co...
199
  			return true;
ab6c27be8   stephen hemminger   netem: fix loss 4...
200
  		} else if (clg->a4 < rnd && rnd < clg->a1 + clg->a4) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
201
  			clg->state = LOST_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
202
  			return true;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
203
204
205
  		} else if (clg->a1 + clg->a4 < rnd) {
  			clg->state = TX_IN_GAP_PERIOD;
  		}
661b79725   stephen hemminger   netem: revised co...
206
207
  
  		break;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
208
  	case TX_IN_BURST_PERIOD:
661b79725   stephen hemminger   netem: revised co...
209
  		if (rnd < clg->a5) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
210
  			clg->state = LOST_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
211
  			return true;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
212
213
214
  		} else {
  			clg->state = TX_IN_BURST_PERIOD;
  		}
661b79725   stephen hemminger   netem: revised co...
215
216
  
  		break;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
217
  	case LOST_IN_GAP_PERIOD:
661b79725   stephen hemminger   netem: revised co...
218
  		if (rnd < clg->a3)
a6e2fe17e   Yang Yingliang   sch_netem: replac...
219
  			clg->state = TX_IN_BURST_PERIOD;
661b79725   stephen hemminger   netem: revised co...
220
  		else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
221
  			clg->state = TX_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
222
  		} else if (clg->a2 + clg->a3 < rnd) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
223
  			clg->state = LOST_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
224
225
226
  			return true;
  		}
  		break;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
227
228
  	case LOST_IN_BURST_PERIOD:
  		clg->state = TX_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
229
230
231
232
233
234
235
236
237
238
  		break;
  	}
  
  	return false;
  }
  
  /* loss_gilb_ell - Gilbert-Elliot model loss generator
   * Generates losses according to the Gilbert-Elliot loss model or
   * its special cases  (Gilbert or Simple Gilbert)
   *
25985edce   Lucas De Marchi   Fix common misspe...
239
   * Makes a comparison between random number and the transition
661b79725   stephen hemminger   netem: revised co...
240
   * probabilities outgoing from the current state, then decides the
25985edce   Lucas De Marchi   Fix common misspe...
241
   * next state. A second random number is extracted and the comparison
661b79725   stephen hemminger   netem: revised co...
242
243
244
245
246
247
248
249
   * with the loss probability of the current state decides if the next
   * packet will be transmitted or lost.
   */
  static bool loss_gilb_ell(struct netem_sched_data *q)
  {
  	struct clgstate *clg = &q->clg;
  
  	switch (clg->state) {
c045a734d   Yang Yingliang   sch_netem: replac...
250
  	case GOOD_STATE:
63862b5be   Aruna-Hewapathirane   net: replace macr...
251
  		if (prandom_u32() < clg->a1)
c045a734d   Yang Yingliang   sch_netem: replac...
252
  			clg->state = BAD_STATE;
63862b5be   Aruna-Hewapathirane   net: replace macr...
253
  		if (prandom_u32() < clg->a4)
661b79725   stephen hemminger   netem: revised co...
254
  			return true;
7c2781fa9   stephen hemminger   netem: missing br...
255
  		break;
c045a734d   Yang Yingliang   sch_netem: replac...
256
  	case BAD_STATE:
63862b5be   Aruna-Hewapathirane   net: replace macr...
257
  		if (prandom_u32() < clg->a2)
c045a734d   Yang Yingliang   sch_netem: replac...
258
  			clg->state = GOOD_STATE;
63862b5be   Aruna-Hewapathirane   net: replace macr...
259
  		if (prandom_u32() > clg->a3)
661b79725   stephen hemminger   netem: revised co...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  			return true;
  	}
  
  	return false;
  }
  
  static bool loss_event(struct netem_sched_data *q)
  {
  	switch (q->loss_model) {
  	case CLG_RANDOM:
  		/* Random packet drop 0 => none, ~0 => all */
  		return q->loss && q->loss >= get_crandom(&q->loss_cor);
  
  	case CLG_4_STATES:
  		/* 4state loss model algorithm (used also for GI model)
  		* Extracts a value from the markov 4 state loss generator,
  		* if it is 1 drops a packet and if needed writes the event in
  		* the kernel logs
  		*/
  		return loss_4state(q);
  
  	case CLG_GILB_ELL:
  		/* Gilbert-Elliot loss model algorithm
  		* Extracts a value from the Gilbert-Elliot loss generator,
  		* if it is 1 drops a packet and if needed writes the event in
  		* the kernel logs
  		*/
  		return loss_gilb_ell(q);
  	}
  
  	return false;	/* not reached */
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
  /* tabledist - return a pseudo-randomly distributed value with mean mu and
   * std deviation sigma.  Uses table lookup to approximate the desired
   * distribution, and a uniformly-distributed pseudo-random source.
   */
b407621c3   Stephen Hemminger   [NETEM]: use bett...
296
297
298
  static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma,
  				struct crndstate *state,
  				const struct disttable *dist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
  {
b407621c3   Stephen Hemminger   [NETEM]: use bett...
300
301
302
  	psched_tdiff_t x;
  	long t;
  	u32 rnd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
309
  
  	if (sigma == 0)
  		return mu;
  
  	rnd = get_crandom(state);
  
  	/* default uniform distribution */
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
310
  	if (dist == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
318
319
320
321
  		return (rnd % (2*sigma)) - sigma + mu;
  
  	t = dist->table[rnd % dist->size];
  	x = (sigma % NETEM_DIST_SCALE) * t;
  	if (x >= 0)
  		x += NETEM_DIST_SCALE/2;
  	else
  		x -= NETEM_DIST_SCALE/2;
  
  	return  x / NETEM_DIST_SCALE + (sigma / NETEM_DIST_SCALE) * t + mu;
  }
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
322
  static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sched_data *q)
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
323
  {
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
324
  	u64 ticks;
fc33cc724   Eric Dumazet   netem: fix build ...
325

90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
326
327
328
329
330
331
332
333
334
335
336
337
338
  	len += q->packet_overhead;
  
  	if (q->cell_size) {
  		u32 cells = reciprocal_divide(len, q->cell_size_reciprocal);
  
  		if (len > cells * q->cell_size)	/* extra cell needed for remainder */
  			cells++;
  		len = cells * (q->cell_size + q->cell_overhead);
  	}
  
  	ticks = (u64)len * NSEC_PER_SEC;
  
  	do_div(ticks, q->rate);
fc33cc724   Eric Dumazet   netem: fix build ...
339
  	return PSCHED_NS2TICKS(ticks);
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
340
  }
ff704050f   stephen hemminger   netem: free skb's...
341
342
343
344
345
346
  static void tfifo_reset(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
  	struct rb_node *p;
  
  	while ((p = rb_first(&q->t_root))) {
37c7cc80b   Eric Dumazet   net: add rb_to_sk...
347
  		struct sk_buff *skb = rb_to_skb(p);
ff704050f   stephen hemminger   netem: free skb's...
348
349
  
  		rb_erase(p, &q->t_root);
2f08a9a16   Eric Dumazet   net_sched: sch_ne...
350
  		rtnl_kfree_skbs(skb, skb);
ff704050f   stephen hemminger   netem: free skb's...
351
352
  	}
  }
960fb66e5   Eric Dumazet   netem: add limita...
353
  static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
50612537e   Eric Dumazet   netem: fix classf...
354
  {
aec0a40a6   Eric Dumazet   netem: use rb tre...
355
  	struct netem_sched_data *q = qdisc_priv(sch);
50612537e   Eric Dumazet   netem: fix classf...
356
  	psched_time_t tnext = netem_skb_cb(nskb)->time_to_send;
aec0a40a6   Eric Dumazet   netem: use rb tre...
357
  	struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
50612537e   Eric Dumazet   netem: fix classf...
358

aec0a40a6   Eric Dumazet   netem: use rb tre...
359
360
  	while (*p) {
  		struct sk_buff *skb;
50612537e   Eric Dumazet   netem: fix classf...
361

aec0a40a6   Eric Dumazet   netem: use rb tre...
362
  		parent = *p;
37c7cc80b   Eric Dumazet   net: add rb_to_sk...
363
  		skb = rb_to_skb(parent);
960fb66e5   Eric Dumazet   netem: add limita...
364
  		if (tnext >= netem_skb_cb(skb)->time_to_send)
aec0a40a6   Eric Dumazet   netem: use rb tre...
365
366
367
  			p = &parent->rb_right;
  		else
  			p = &parent->rb_left;
50612537e   Eric Dumazet   netem: fix classf...
368
  	}
56b174256   Eric Dumazet   net: add rbnode t...
369
370
  	rb_link_node(&nskb->rbnode, parent, p);
  	rb_insert_color(&nskb->rbnode, &q->t_root);
aec0a40a6   Eric Dumazet   netem: use rb tre...
371
  	sch->q.qlen++;
50612537e   Eric Dumazet   netem: fix classf...
372
  }
6071bd1aa   Neil Horman   netem: Segment GS...
373
374
375
376
  /* netem can't properly corrupt a megapacket (like we get from GSO), so instead
   * when we statistically choose to corrupt one, we instead segment it, returning
   * the first packet to be corrupted, and re-enqueue the remaining frames
   */
520ac30f4   Eric Dumazet   net_sched: drop p...
377
378
  static struct sk_buff *netem_segment(struct sk_buff *skb, struct Qdisc *sch,
  				     struct sk_buff **to_free)
6071bd1aa   Neil Horman   netem: Segment GS...
379
380
381
382
383
384
385
  {
  	struct sk_buff *segs;
  	netdev_features_t features = netif_skb_features(skb);
  
  	segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
  
  	if (IS_ERR_OR_NULL(segs)) {
520ac30f4   Eric Dumazet   net_sched: drop p...
386
  		qdisc_drop(skb, sch, to_free);
6071bd1aa   Neil Horman   netem: Segment GS...
387
388
389
390
391
  		return NULL;
  	}
  	consume_skb(skb);
  	return segs;
  }
48da34b7a   Florian Westphal   sched: add and us...
392
393
394
395
396
397
398
399
400
  static void netem_enqueue_skb_head(struct qdisc_skb_head *qh, struct sk_buff *skb)
  {
  	skb->next = qh->head;
  
  	if (!qh->head)
  		qh->tail = skb;
  	qh->head = skb;
  	qh->qlen++;
  }
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
401
402
403
404
405
406
  /*
   * Insert one skb into qdisc.
   * Note: parent depends on return value to account for queue length.
   * 	NET_XMIT_DROP: queue length didn't change.
   *      NET_XMIT_SUCCESS: one skb was queued.
   */
520ac30f4   Eric Dumazet   net_sched: drop p...
407
408
  static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
  			 struct sk_buff **to_free)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
89e1df74f   Guillaume Chazarain   [PKT_SCHED] netem...
411
412
  	/* We don't fill cb now as skb_unshare() may invalidate it */
  	struct netem_skb_cb *cb;
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
413
  	struct sk_buff *skb2;
6071bd1aa   Neil Horman   netem: Segment GS...
414
415
416
  	struct sk_buff *segs = NULL;
  	unsigned int len = 0, last_len, prev_len = qdisc_pkt_len(skb);
  	int nb = 0;
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
417
  	int count = 1;
6071bd1aa   Neil Horman   netem: Segment GS...
418
  	int rc = NET_XMIT_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419

0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
420
421
422
  	/* Random duplication */
  	if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
  		++count;
661b79725   stephen hemminger   netem: revised co...
423
  	/* Drop packet? */
e4ae004b8   Eric Dumazet   netem: add ECN ca...
424
425
  	if (loss_event(q)) {
  		if (q->ecn && INET_ECN_set_ce(skb))
25331d6ce   John Fastabend   net: sched: imple...
426
  			qdisc_qstats_drop(sch); /* mark packet */
e4ae004b8   Eric Dumazet   netem: add ECN ca...
427
428
429
  		else
  			--count;
  	}
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
430
  	if (count == 0) {
25331d6ce   John Fastabend   net: sched: imple...
431
  		qdisc_qstats_drop(sch);
520ac30f4   Eric Dumazet   net_sched: drop p...
432
  		__qdisc_drop(skb, to_free);
c27f339af   Jarek Poplawski   net_sched: Add qd...
433
  		return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
  	}
5a308f40b   Eric Dumazet   netem: refine ear...
435
436
  	/* If a delay is expected, orphan the skb. (orphaning usually takes
  	 * place at TX completion time, so _before_ the link transit delay)
5a308f40b   Eric Dumazet   netem: refine ear...
437
  	 */
5080f39e8   Nik Unger   netem: apply corr...
438
  	if (q->latency || q->jitter || q->rate)
f2f872f92   Eric Dumazet   netem: Introduce ...
439
  		skb_orphan_partial(skb);
4e8a52015   David S. Miller   [PKT_SCHED] netem...
440

0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
441
442
443
444
445
446
  	/*
  	 * If we need to duplicate packet, then re-insert at top of the
  	 * qdisc tree, since parent queuer expects that only one
  	 * skb will be queued.
  	 */
  	if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
7698b4fca   David S. Miller   pkt_sched: Add an...
447
  		struct Qdisc *rootq = qdisc_root(sch);
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
448
  		u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
449

b396cca6f   Eric Dumazet   net: sched: depre...
450
  		q->duplicate = 0;
520ac30f4   Eric Dumazet   net_sched: drop p...
451
  		rootq->enqueue(skb2, rootq, to_free);
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
452
  		q->duplicate = dupsave;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  	}
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
454
455
456
457
458
459
460
  	/*
  	 * Randomized packet corruption.
  	 * Make copy if needed since we are modifying
  	 * If packet is going to be hardware checksummed, then
  	 * do it now in software before we mangle it.
  	 */
  	if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
6071bd1aa   Neil Horman   netem: Segment GS...
461
  		if (skb_is_gso(skb)) {
520ac30f4   Eric Dumazet   net_sched: drop p...
462
  			segs = netem_segment(skb, sch, to_free);
6071bd1aa   Neil Horman   netem: Segment GS...
463
464
465
466
467
468
469
470
  			if (!segs)
  				return NET_XMIT_DROP;
  		} else {
  			segs = skb;
  		}
  
  		skb = segs;
  		segs = segs->next;
8a6e9c670   Eric Dumazet   net_sched: netem:...
471
472
473
474
475
476
477
478
  		skb = skb_unshare(skb, GFP_ATOMIC);
  		if (unlikely(!skb)) {
  			qdisc_qstats_drop(sch);
  			goto finish_segs;
  		}
  		if (skb->ip_summed == CHECKSUM_PARTIAL &&
  		    skb_checksum_help(skb)) {
  			qdisc_drop(skb, sch, to_free);
6071bd1aa   Neil Horman   netem: Segment GS...
479
480
  			goto finish_segs;
  		}
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
481

63862b5be   Aruna-Hewapathirane   net: replace macr...
482
483
  		skb->data[prandom_u32() % skb_headlen(skb)] ^=
  			1<<(prandom_u32() % 8);
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
484
  	}
97d0678f9   Florian Westphal   sched: don't use ...
485
  	if (unlikely(sch->q.qlen >= sch->limit))
f77ff13a0   Alexey Kodanev   sch_netem: fix sk...
486
  		return qdisc_drop_all(skb, sch, to_free);
960fb66e5   Eric Dumazet   netem: add limita...
487

25331d6ce   John Fastabend   net: sched: imple...
488
  	qdisc_qstats_backlog_inc(sch, skb);
960fb66e5   Eric Dumazet   netem: add limita...
489

5f86173bd   Jussi Kivilinna   net_sched: Add qd...
490
  	cb = netem_skb_cb(skb);
cc7ec456f   Eric Dumazet   net_sched: cleanups
491
  	if (q->gap == 0 ||		/* not doing reordering */
a42b4799c   Vijay Subramanian   netem: Fix off-by...
492
  	    q->counter < q->gap - 1 ||	/* inside last reordering gap */
f64f9e719   Joe Perches   net: Move && and ...
493
  	    q->reorder < get_crandom(&q->reorder_cor)) {
0f9f32ac6   Stephen Hemminger   [PKT_SCHED] netem...
494
  		psched_time_t now;
07aaa1154   Stephen Hemminger   [NETEM]: use PSCH...
495
496
497
498
  		psched_tdiff_t delay;
  
  		delay = tabledist(q->latency, q->jitter,
  				  &q->delay_cor, q->delay_dist);
3bebcda28   Patrick McHardy   [NET_SCHED]: turn...
499
  		now = psched_get_time();
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
500
501
  
  		if (q->rate) {
5080f39e8   Nik Unger   netem: apply corr...
502
503
504
505
506
507
508
  			struct netem_skb_cb *last = NULL;
  
  			if (sch->q.tail)
  				last = netem_skb_cb(sch->q.tail);
  			if (q->t_root.rb_node) {
  				struct sk_buff *t_skb;
  				struct netem_skb_cb *t_last;
37c7cc80b   Eric Dumazet   net: add rb_to_sk...
509
  				t_skb = skb_rb_last(&q->t_root);
5080f39e8   Nik Unger   netem: apply corr...
510
511
512
513
514
515
  				t_last = netem_skb_cb(t_skb);
  				if (!last ||
  				    t_last->time_to_send > last->time_to_send) {
  					last = t_last;
  				}
  			}
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
516

aec0a40a6   Eric Dumazet   netem: use rb tre...
517
  			if (last) {
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
518
  				/*
a13d31047   Johannes Naab   netem: fix delay ...
519
520
  				 * Last packet in queue is reference point (now),
  				 * calculate this time bonus and subtract
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
521
522
  				 * from delay.
  				 */
5080f39e8   Nik Unger   netem: apply corr...
523
  				delay -= last->time_to_send - now;
a13d31047   Johannes Naab   netem: fix delay ...
524
  				delay = max_t(psched_tdiff_t, 0, delay);
5080f39e8   Nik Unger   netem: apply corr...
525
  				now = last->time_to_send;
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
526
  			}
a13d31047   Johannes Naab   netem: fix delay ...
527

8cfd88d6d   Yang Yingliang   sch_netem: more p...
528
  			delay += packet_len_2_sched_time(qdisc_pkt_len(skb), q);
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
529
  		}
7c59e25f3   Patrick McHardy   [NET_SCHED]: kill...
530
  		cb->time_to_send = now + delay;
aec0a40a6   Eric Dumazet   netem: use rb tre...
531
  		cb->tstamp_save = skb->tstamp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
  		++q->counter;
960fb66e5   Eric Dumazet   netem: add limita...
533
  		tfifo_enqueue(skb, sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
  	} else {
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
535
  		/*
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
536
537
538
  		 * Do re-ordering by putting one out of N packets at the front
  		 * of the queue.
  		 */
3bebcda28   Patrick McHardy   [NET_SCHED]: turn...
539
  		cb->time_to_send = psched_get_time();
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
540
  		q->counter = 0;
8ba25dad0   Jarek Poplawski   sch_netem: Replac...
541

48da34b7a   Florian Westphal   sched: add and us...
542
  		netem_enqueue_skb_head(&sch->q, skb);
eb1019244   Hagen Paul Pfeifer   net_sched: Bug in...
543
  		sch->qstats.requeues++;
378a2f090   Jarek Poplawski   net_sched: Add qd...
544
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545

6071bd1aa   Neil Horman   netem: Segment GS...
546
547
548
549
550
551
552
  finish_segs:
  	if (segs) {
  		while (segs) {
  			skb2 = segs->next;
  			segs->next = NULL;
  			qdisc_skb_cb(segs)->pkt_len = segs->len;
  			last_len = segs->len;
520ac30f4   Eric Dumazet   net_sched: drop p...
553
  			rc = qdisc_enqueue(segs, sch, to_free);
6071bd1aa   Neil Horman   netem: Segment GS...
554
555
556
557
558
559
560
561
562
563
564
565
566
  			if (rc != NET_XMIT_SUCCESS) {
  				if (net_xmit_drop_count(rc))
  					qdisc_qstats_drop(sch);
  			} else {
  				nb++;
  				len += last_len;
  			}
  			segs = skb2;
  		}
  		sch->q.qlen += nb;
  		if (nb > 1)
  			qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len);
  	}
10f6dfcfd   stephen hemminger   Revert "sch_netem...
567
  	return NET_XMIT_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
572
  static struct sk_buff *netem_dequeue(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
  	struct sk_buff *skb;
aec0a40a6   Eric Dumazet   netem: use rb tre...
573
  	struct rb_node *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574

50612537e   Eric Dumazet   netem: fix classf...
575
  tfifo_dequeue:
ed760cb8a   Florian Westphal   sched: replace __...
576
  	skb = __qdisc_dequeue_head(&sch->q);
771018e76   Stephen Hemminger   [PKT_SCHED]: nete...
577
  	if (skb) {
25331d6ce   John Fastabend   net: sched: imple...
578
  		qdisc_qstats_backlog_dec(sch, skb);
0ad2a8365   Beshay, Joseph   netem: Fixes byte...
579
  deliver:
aec0a40a6   Eric Dumazet   netem: use rb tre...
580
581
582
583
584
  		qdisc_bstats_update(sch, skb);
  		return skb;
  	}
  	p = rb_first(&q->t_root);
  	if (p) {
36b7bfe09   Eric Dumazet   netem: fix possib...
585
  		psched_time_t time_to_send;
37c7cc80b   Eric Dumazet   net: add rb_to_sk...
586
  		skb = rb_to_skb(p);
0f9f32ac6   Stephen Hemminger   [PKT_SCHED] netem...
587
588
  
  		/* if more time remaining? */
36b7bfe09   Eric Dumazet   netem: fix possib...
589
590
  		time_to_send = netem_skb_cb(skb)->time_to_send;
  		if (time_to_send <= psched_get_time()) {
aec0a40a6   Eric Dumazet   netem: use rb tre...
591
592
593
  			rb_erase(p, &q->t_root);
  
  			sch->q.qlen--;
0ad2a8365   Beshay, Joseph   netem: Fixes byte...
594
  			qdisc_qstats_backlog_dec(sch, skb);
aec0a40a6   Eric Dumazet   netem: use rb tre...
595
596
597
  			skb->next = NULL;
  			skb->prev = NULL;
  			skb->tstamp = netem_skb_cb(skb)->tstamp_save;
03c05f0d4   Jarek Poplawski   pkt_sched: Use qd...
598

8caf15397   Jarek Poplawski   net: sch_netem: F...
599
600
601
602
603
  #ifdef CONFIG_NET_CLS_ACT
  			/*
  			 * If it's at ingress let's pretend the delay is
  			 * from the network (tstamp will be updated).
  			 */
bc31c905e   Willem de Bruijn   net-tc: convert t...
604
  			if (skb->tc_redirected && skb->tc_from_ingress)
2456e8553   Thomas Gleixner   ktime: Get rid of...
605
  				skb->tstamp = 0;
8caf15397   Jarek Poplawski   net: sch_netem: F...
606
  #endif
10f6dfcfd   stephen hemminger   Revert "sch_netem...
607

50612537e   Eric Dumazet   netem: fix classf...
608
  			if (q->qdisc) {
21de12ee5   Eric Dumazet   netem: fix a use ...
609
  				unsigned int pkt_len = qdisc_pkt_len(skb);
520ac30f4   Eric Dumazet   net_sched: drop p...
610
611
  				struct sk_buff *to_free = NULL;
  				int err;
50612537e   Eric Dumazet   netem: fix classf...
612

520ac30f4   Eric Dumazet   net_sched: drop p...
613
614
  				err = qdisc_enqueue(skb, q->qdisc, &to_free);
  				kfree_skb_list(to_free);
21de12ee5   Eric Dumazet   netem: fix a use ...
615
616
617
618
619
  				if (err != NET_XMIT_SUCCESS &&
  				    net_xmit_drop_count(err)) {
  					qdisc_qstats_drop(sch);
  					qdisc_tree_reduce_backlog(sch, 1,
  								  pkt_len);
50612537e   Eric Dumazet   netem: fix classf...
620
621
622
  				}
  				goto tfifo_dequeue;
  			}
aec0a40a6   Eric Dumazet   netem: use rb tre...
623
  			goto deliver;
07aaa1154   Stephen Hemminger   [NETEM]: use PSCH...
624
  		}
11274e5a4   Stephen Hemminger   [NETEM]: avoid ex...
625

50612537e   Eric Dumazet   netem: fix classf...
626
627
628
629
630
  		if (q->qdisc) {
  			skb = q->qdisc->ops->dequeue(q->qdisc);
  			if (skb)
  				goto deliver;
  		}
36b7bfe09   Eric Dumazet   netem: fix possib...
631
  		qdisc_watchdog_schedule(&q->watchdog, time_to_send);
0f9f32ac6   Stephen Hemminger   [PKT_SCHED] netem...
632
  	}
50612537e   Eric Dumazet   netem: fix classf...
633
634
635
636
637
  	if (q->qdisc) {
  		skb = q->qdisc->ops->dequeue(q->qdisc);
  		if (skb)
  			goto deliver;
  	}
0f9f32ac6   Stephen Hemminger   [PKT_SCHED] netem...
638
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
  static void netem_reset(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
50612537e   Eric Dumazet   netem: fix classf...
643
  	qdisc_reset_queue(sch);
ff704050f   stephen hemminger   netem: free skb's...
644
  	tfifo_reset(sch);
50612537e   Eric Dumazet   netem: fix classf...
645
646
  	if (q->qdisc)
  		qdisc_reset(q->qdisc);
59cb5c673   Patrick McHardy   [NET_SCHED]: sch_...
647
  	qdisc_watchdog_cancel(&q->watchdog);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
  }
6373a9a28   stephen hemminger   netem: use vmallo...
649
650
  static void dist_free(struct disttable *d)
  {
4cb28970a   WANG Cong   net: use the new ...
651
  	kvfree(d);
6373a9a28   stephen hemminger   netem: use vmallo...
652
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
654
655
656
  /*
   * Distribution data is a variable size payload containing
   * signed 16 bit values.
   */
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
657
  static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
6373a9a28   stephen hemminger   netem: use vmallo...
660
  	size_t n = nla_len(attr)/sizeof(__s16);
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
661
  	const __s16 *data = nla_data(attr);
7698b4fca   David S. Miller   pkt_sched: Add an...
662
  	spinlock_t *root_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
  	struct disttable *d;
  	int i;
df173bda2   stephen hemminger   netem: define NET...
665
  	if (n > NETEM_DIST_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
  		return -EINVAL;
752ade68c   Michal Hocko   treewide: use kv[...
667
  	d = kvmalloc(sizeof(struct disttable) + n * sizeof(s16), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
670
671
672
673
  	if (!d)
  		return -ENOMEM;
  
  	d->size = n;
  	for (i = 0; i < n; i++)
  		d->table[i] = data[i];
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
674

102396ae6   Jarek Poplawski   pkt_sched: Fix lo...
675
  	root_lock = qdisc_root_sleeping_lock(sch);
7698b4fca   David S. Miller   pkt_sched: Add an...
676
677
  
  	spin_lock_bh(root_lock);
bb52c7acf   Eric Dumazet   netem: dont call ...
678
  	swap(q->delay_dist, d);
7698b4fca   David S. Miller   pkt_sched: Add an...
679
  	spin_unlock_bh(root_lock);
bb52c7acf   Eric Dumazet   netem: dont call ...
680
681
  
  	dist_free(d);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682
683
  	return 0;
  }
49545a777   Yang Yingliang   sch_netem: change...
684
  static void get_correlation(struct netem_sched_data *q, const struct nlattr *attr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
  {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
686
  	const struct tc_netem_corr *c = nla_data(attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
690
  	init_crandom(&q->delay_cor, c->delay_corr);
  	init_crandom(&q->loss_cor, c->loss_corr);
  	init_crandom(&q->dup_cor, c->dup_corr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  }
49545a777   Yang Yingliang   sch_netem: change...
692
  static void get_reorder(struct netem_sched_data *q, const struct nlattr *attr)
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
693
  {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
694
  	const struct tc_netem_reorder *r = nla_data(attr);
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
695

0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
696
697
  	q->reorder = r->probability;
  	init_crandom(&q->reorder_cor, r->correlation);
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
698
  }
49545a777   Yang Yingliang   sch_netem: change...
699
  static void get_corrupt(struct netem_sched_data *q, const struct nlattr *attr)
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
700
  {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
701
  	const struct tc_netem_corrupt *r = nla_data(attr);
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
702

c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
703
704
  	q->corrupt = r->probability;
  	init_crandom(&q->corrupt_cor, r->correlation);
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
705
  }
49545a777   Yang Yingliang   sch_netem: change...
706
  static void get_rate(struct netem_sched_data *q, const struct nlattr *attr)
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
707
  {
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
708
709
710
  	const struct tc_netem_rate *r = nla_data(attr);
  
  	q->rate = r->rate;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
711
712
  	q->packet_overhead = r->packet_overhead;
  	q->cell_size = r->cell_size;
809fa972f   Hannes Frederic Sowa   reciprocal_divide...
713
  	q->cell_overhead = r->cell_overhead;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
714
715
  	if (q->cell_size)
  		q->cell_size_reciprocal = reciprocal_value(q->cell_size);
809fa972f   Hannes Frederic Sowa   reciprocal_divide...
716
717
  	else
  		q->cell_size_reciprocal = (struct reciprocal_value) { 0 };
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
718
  }
49545a777   Yang Yingliang   sch_netem: change...
719
  static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr)
661b79725   stephen hemminger   netem: revised co...
720
  {
661b79725   stephen hemminger   netem: revised co...
721
722
723
724
725
  	const struct nlattr *la;
  	int rem;
  
  	nla_for_each_nested(la, attr, rem) {
  		u16 type = nla_type(la);
833fa7438   Yang Yingliang   net_sched: add sp...
726
  		switch (type) {
661b79725   stephen hemminger   netem: revised co...
727
728
  		case NETEM_LOSS_GI: {
  			const struct tc_netem_gimodel *gi = nla_data(la);
2494654d4   stephen hemminger   netem: loss model...
729
  			if (nla_len(la) < sizeof(struct tc_netem_gimodel)) {
661b79725   stephen hemminger   netem: revised co...
730
731
732
733
734
735
  				pr_info("netem: incorrect gi model size
  ");
  				return -EINVAL;
  			}
  
  			q->loss_model = CLG_4_STATES;
3fbac2a87   Yang Yingliang   sch_netem: replac...
736
  			q->clg.state = TX_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
737
738
739
740
741
742
743
744
745
746
  			q->clg.a1 = gi->p13;
  			q->clg.a2 = gi->p31;
  			q->clg.a3 = gi->p32;
  			q->clg.a4 = gi->p14;
  			q->clg.a5 = gi->p23;
  			break;
  		}
  
  		case NETEM_LOSS_GE: {
  			const struct tc_netem_gemodel *ge = nla_data(la);
2494654d4   stephen hemminger   netem: loss model...
747
748
749
  			if (nla_len(la) < sizeof(struct tc_netem_gemodel)) {
  				pr_info("netem: incorrect ge model size
  ");
661b79725   stephen hemminger   netem: revised co...
750
751
752
753
  				return -EINVAL;
  			}
  
  			q->loss_model = CLG_GILB_ELL;
3fbac2a87   Yang Yingliang   sch_netem: replac...
754
  			q->clg.state = GOOD_STATE;
661b79725   stephen hemminger   netem: revised co...
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
  			q->clg.a1 = ge->p;
  			q->clg.a2 = ge->r;
  			q->clg.a3 = ge->h;
  			q->clg.a4 = ge->k1;
  			break;
  		}
  
  		default:
  			pr_info("netem: unknown loss type %u
  ", type);
  			return -EINVAL;
  		}
  	}
  
  	return 0;
  }
27a3421e4   Patrick McHardy   [NET_SCHED]: Use ...
771
772
773
774
  static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = {
  	[TCA_NETEM_CORR]	= { .len = sizeof(struct tc_netem_corr) },
  	[TCA_NETEM_REORDER]	= { .len = sizeof(struct tc_netem_reorder) },
  	[TCA_NETEM_CORRUPT]	= { .len = sizeof(struct tc_netem_corrupt) },
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
775
  	[TCA_NETEM_RATE]	= { .len = sizeof(struct tc_netem_rate) },
661b79725   stephen hemminger   netem: revised co...
776
  	[TCA_NETEM_LOSS]	= { .type = NLA_NESTED },
e4ae004b8   Eric Dumazet   netem: add ECN ca...
777
  	[TCA_NETEM_ECN]		= { .type = NLA_U32 },
6a031f67c   Yang Yingliang   sch_netem: suppor...
778
  	[TCA_NETEM_RATE64]	= { .type = NLA_U64 },
27a3421e4   Patrick McHardy   [NET_SCHED]: Use ...
779
  };
2c10b32bf   Thomas Graf   netlink: Remove c...
780
781
782
783
  static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
  		      const struct nla_policy *policy, int len)
  {
  	int nested_len = nla_len(nla) - NLA_ALIGN(len);
661b79725   stephen hemminger   netem: revised co...
784
785
786
  	if (nested_len < 0) {
  		pr_info("netem: invalid attributes len %d
  ", nested_len);
2c10b32bf   Thomas Graf   netlink: Remove c...
787
  		return -EINVAL;
661b79725   stephen hemminger   netem: revised co...
788
  	}
2c10b32bf   Thomas Graf   netlink: Remove c...
789
790
  	if (nested_len >= nla_attr_size(0))
  		return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len),
fceb6435e   Johannes Berg   netlink: pass ext...
791
  				 nested_len, policy, NULL);
661b79725   stephen hemminger   netem: revised co...
792

2c10b32bf   Thomas Graf   netlink: Remove c...
793
794
795
  	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
  	return 0;
  }
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
796
  /* Parse netlink message to set options */
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
797
  static int netem_change(struct Qdisc *sch, struct nlattr *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
b03f46720   Patrick McHardy   [NET_SCHED]: sch_...
800
  	struct nlattr *tb[TCA_NETEM_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
  	struct tc_netem_qopt *qopt;
54a4b05cd   Yang Yingliang   sch_netem: return...
802
803
  	struct clgstate old_clg;
  	int old_loss_model = CLG_RANDOM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
  	int ret;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
805

b03f46720   Patrick McHardy   [NET_SCHED]: sch_...
806
  	if (opt == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
  		return -EINVAL;
2c10b32bf   Thomas Graf   netlink: Remove c...
808
809
  	qopt = nla_data(opt);
  	ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt));
b03f46720   Patrick McHardy   [NET_SCHED]: sch_...
810
811
  	if (ret < 0)
  		return ret;
54a4b05cd   Yang Yingliang   sch_netem: return...
812
813
814
815
816
  	/* backup q->clg and q->loss_model */
  	old_clg = q->clg;
  	old_loss_model = q->loss_model;
  
  	if (tb[TCA_NETEM_LOSS]) {
49545a777   Yang Yingliang   sch_netem: change...
817
  		ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]);
54a4b05cd   Yang Yingliang   sch_netem: return...
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
  		if (ret) {
  			q->loss_model = old_loss_model;
  			return ret;
  		}
  	} else {
  		q->loss_model = CLG_RANDOM;
  	}
  
  	if (tb[TCA_NETEM_DELAY_DIST]) {
  		ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]);
  		if (ret) {
  			/* recover clg and loss_model, in case of
  			 * q->clg and q->loss_model were modified
  			 * in get_loss_clg()
  			 */
  			q->clg = old_clg;
  			q->loss_model = old_loss_model;
  			return ret;
  		}
  	}
50612537e   Eric Dumazet   netem: fix classf...
838
  	sch->limit = qopt->limit;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
839

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
841
842
843
  	q->latency = qopt->latency;
  	q->jitter = qopt->jitter;
  	q->limit = qopt->limit;
  	q->gap = qopt->gap;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
844
  	q->counter = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
  	q->loss = qopt->loss;
  	q->duplicate = qopt->duplicate;
bb2f8cc0e   Stephen Hemminger   [NETEM]: spelling...
847
848
  	/* for compatibility with earlier versions.
  	 * if gap is set, need to assume 100% probability
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
849
  	 */
a362e0a78   Stephen Hemminger   [NETEM]: report r...
850
851
  	if (q->gap)
  		q->reorder = ~0;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
852

265eb67fb   Stephen Hemminger   netem: eliminate ...
853
  	if (tb[TCA_NETEM_CORR])
49545a777   Yang Yingliang   sch_netem: change...
854
  		get_correlation(q, tb[TCA_NETEM_CORR]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855

265eb67fb   Stephen Hemminger   netem: eliminate ...
856
  	if (tb[TCA_NETEM_REORDER])
49545a777   Yang Yingliang   sch_netem: change...
857
  		get_reorder(q, tb[TCA_NETEM_REORDER]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858

265eb67fb   Stephen Hemminger   netem: eliminate ...
859
  	if (tb[TCA_NETEM_CORRUPT])
49545a777   Yang Yingliang   sch_netem: change...
860
  		get_corrupt(q, tb[TCA_NETEM_CORRUPT]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861

7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
862
  	if (tb[TCA_NETEM_RATE])
49545a777   Yang Yingliang   sch_netem: change...
863
  		get_rate(q, tb[TCA_NETEM_RATE]);
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
864

6a031f67c   Yang Yingliang   sch_netem: suppor...
865
866
867
  	if (tb[TCA_NETEM_RATE64])
  		q->rate = max_t(u64, q->rate,
  				nla_get_u64(tb[TCA_NETEM_RATE64]));
e4ae004b8   Eric Dumazet   netem: add ECN ca...
868
869
  	if (tb[TCA_NETEM_ECN])
  		q->ecn = nla_get_u32(tb[TCA_NETEM_ECN]);
661b79725   stephen hemminger   netem: revised co...
870
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
  }
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
872
  static int netem_init(struct Qdisc *sch, struct nlattr *opt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
  	int ret;
634576a18   Nikolay Aleksandrov   sch_netem: avoid ...
876
  	qdisc_watchdog_init(&q->watchdog, sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
878
  	if (!opt)
  		return -EINVAL;
661b79725   stephen hemminger   netem: revised co...
879
  	q->loss_model = CLG_RANDOM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
  	ret = netem_change(sch, opt);
50612537e   Eric Dumazet   netem: fix classf...
881
  	if (ret)
250a65f78   stephen hemminger   netem: update ver...
882
883
  		pr_info("netem: change failed
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
885
886
887
888
889
  	return ret;
  }
  
  static void netem_destroy(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
59cb5c673   Patrick McHardy   [NET_SCHED]: sch_...
890
  	qdisc_watchdog_cancel(&q->watchdog);
50612537e   Eric Dumazet   netem: fix classf...
891
892
  	if (q->qdisc)
  		qdisc_destroy(q->qdisc);
6373a9a28   stephen hemminger   netem: use vmallo...
893
  	dist_free(q->delay_dist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
  }
661b79725   stephen hemminger   netem: revised co...
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
  static int dump_loss_model(const struct netem_sched_data *q,
  			   struct sk_buff *skb)
  {
  	struct nlattr *nest;
  
  	nest = nla_nest_start(skb, TCA_NETEM_LOSS);
  	if (nest == NULL)
  		goto nla_put_failure;
  
  	switch (q->loss_model) {
  	case CLG_RANDOM:
  		/* legacy loss model */
  		nla_nest_cancel(skb, nest);
  		return 0;	/* no data */
  
  	case CLG_4_STATES: {
  		struct tc_netem_gimodel gi = {
  			.p13 = q->clg.a1,
  			.p31 = q->clg.a2,
  			.p32 = q->clg.a3,
  			.p14 = q->clg.a4,
  			.p23 = q->clg.a5,
  		};
1b34ec43c   David S. Miller   pkt_sched: Stop u...
918
919
  		if (nla_put(skb, NETEM_LOSS_GI, sizeof(gi), &gi))
  			goto nla_put_failure;
661b79725   stephen hemminger   netem: revised co...
920
921
922
923
924
925
926
927
928
  		break;
  	}
  	case CLG_GILB_ELL: {
  		struct tc_netem_gemodel ge = {
  			.p = q->clg.a1,
  			.r = q->clg.a2,
  			.h = q->clg.a3,
  			.k1 = q->clg.a4,
  		};
1b34ec43c   David S. Miller   pkt_sched: Stop u...
929
930
  		if (nla_put(skb, NETEM_LOSS_GE, sizeof(ge), &ge))
  			goto nla_put_failure;
661b79725   stephen hemminger   netem: revised co...
931
932
933
934
935
936
937
938
939
940
941
  		break;
  	}
  	}
  
  	nla_nest_end(skb, nest);
  	return 0;
  
  nla_put_failure:
  	nla_nest_cancel(skb, nest);
  	return -1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
944
  static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
  {
  	const struct netem_sched_data *q = qdisc_priv(sch);
861d7f745   stephen hemminger   netem: cleanup du...
945
  	struct nlattr *nla = (struct nlattr *) skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946
947
  	struct tc_netem_qopt qopt;
  	struct tc_netem_corr cor;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
948
  	struct tc_netem_reorder reorder;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
949
  	struct tc_netem_corrupt corrupt;
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
950
  	struct tc_netem_rate rate;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
951
952
953
954
955
956
957
  
  	qopt.latency = q->latency;
  	qopt.jitter = q->jitter;
  	qopt.limit = q->limit;
  	qopt.loss = q->loss;
  	qopt.gap = q->gap;
  	qopt.duplicate = q->duplicate;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
958
959
  	if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
962
963
  
  	cor.delay_corr = q->delay_cor.rho;
  	cor.loss_corr = q->loss_cor.rho;
  	cor.dup_corr = q->dup_cor.rho;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
964
965
  	if (nla_put(skb, TCA_NETEM_CORR, sizeof(cor), &cor))
  		goto nla_put_failure;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
966
967
968
  
  	reorder.probability = q->reorder;
  	reorder.correlation = q->reorder_cor.rho;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
969
970
  	if (nla_put(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder))
  		goto nla_put_failure;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
971

c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
972
973
  	corrupt.probability = q->corrupt;
  	corrupt.correlation = q->corrupt_cor.rho;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
974
975
  	if (nla_put(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt))
  		goto nla_put_failure;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
976

6a031f67c   Yang Yingliang   sch_netem: suppor...
977
  	if (q->rate >= (1ULL << 32)) {
2a51c1e8e   Nicolas Dichtel   sched: use nla_pu...
978
979
  		if (nla_put_u64_64bit(skb, TCA_NETEM_RATE64, q->rate,
  				      TCA_NETEM_PAD))
6a031f67c   Yang Yingliang   sch_netem: suppor...
980
981
982
983
984
  			goto nla_put_failure;
  		rate.rate = ~0U;
  	} else {
  		rate.rate = q->rate;
  	}
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
985
986
987
  	rate.packet_overhead = q->packet_overhead;
  	rate.cell_size = q->cell_size;
  	rate.cell_overhead = q->cell_overhead;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
988
989
  	if (nla_put(skb, TCA_NETEM_RATE, sizeof(rate), &rate))
  		goto nla_put_failure;
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
990

e4ae004b8   Eric Dumazet   netem: add ECN ca...
991
992
  	if (q->ecn && nla_put_u32(skb, TCA_NETEM_ECN, q->ecn))
  		goto nla_put_failure;
661b79725   stephen hemminger   netem: revised co...
993
994
  	if (dump_loss_model(q, skb) != 0)
  		goto nla_put_failure;
861d7f745   stephen hemminger   netem: cleanup du...
995
  	return nla_nest_end(skb, nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
996

1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
997
  nla_put_failure:
861d7f745   stephen hemminger   netem: cleanup du...
998
  	nlmsg_trim(skb, nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999
1000
  	return -1;
  }
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1001
1002
1003
1004
  static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
  			  struct sk_buff *skb, struct tcmsg *tcm)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
50612537e   Eric Dumazet   netem: fix classf...
1005
  	if (cl != 1 || !q->qdisc) 	/* only one class */
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
  		return -ENOENT;
  
  	tcm->tcm_handle |= TC_H_MIN(1);
  	tcm->tcm_info = q->qdisc->handle;
  
  	return 0;
  }
  
  static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
  		     struct Qdisc **old)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
86a7996cc   WANG Cong   net_sched: introd...
1018
  	*old = qdisc_replace(sch, new, &q->qdisc);
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1019
1020
1021
1022
1023
1024
1025
1026
  	return 0;
  }
  
  static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
  	return q->qdisc;
  }
143976ce9   WANG Cong   net_sched: remove...
1027
  static unsigned long netem_find(struct Qdisc *sch, u32 classid)
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1028
1029
1030
  {
  	return 1;
  }
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
  static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker)
  {
  	if (!walker->stop) {
  		if (walker->count >= walker->skip)
  			if (walker->fn(sch, 1, walker) < 0) {
  				walker->stop = 1;
  				return;
  			}
  		walker->count++;
  	}
  }
  
  static const struct Qdisc_class_ops netem_class_ops = {
  	.graft		=	netem_graft,
  	.leaf		=	netem_leaf,
143976ce9   WANG Cong   net_sched: remove...
1046
  	.find		=	netem_find,
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1047
1048
1049
  	.walk		=	netem_walk,
  	.dump		=	netem_dump_class,
  };
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
1050
  static struct Qdisc_ops netem_qdisc_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
  	.id		=	"netem",
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1052
  	.cl_ops		=	&netem_class_ops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1053
1054
1055
  	.priv_size	=	sizeof(struct netem_sched_data),
  	.enqueue	=	netem_enqueue,
  	.dequeue	=	netem_dequeue,
77be155cb   Jarek Poplawski   pkt_sched: Add pe...
1056
  	.peek		=	qdisc_peek_dequeued,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
  	.init		=	netem_init,
  	.reset		=	netem_reset,
  	.destroy	=	netem_destroy,
  	.change		=	netem_change,
  	.dump		=	netem_dump,
  	.owner		=	THIS_MODULE,
  };
  
  
  static int __init netem_module_init(void)
  {
eb229c4cd   Stephen Hemminger   [NETEM]: Add vers...
1068
1069
  	pr_info("netem: version " VERSION "
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
1072
1073
1074
1075
1076
1077
1078
  	return register_qdisc(&netem_qdisc_ops);
  }
  static void __exit netem_module_exit(void)
  {
  	unregister_qdisc(&netem_qdisc_ops);
  }
  module_init(netem_module_init)
  module_exit(netem_module_exit)
  MODULE_LICENSE("GPL");