Blame view

net/sched/sch_netem.c 31.3 KB
84a14ae8c   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
  /*
   * net/sched/sch_netem.c	Network emulator
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
   *  		Many of the algorithms and ideas for this came from
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
6
   *		NIST Net which is not copyrighted.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
10
   *
   * Authors:	Stephen Hemminger <shemminger@osdl.org>
   *		Catalin(ux aka Dino) BOIE <catab at umbrella dot ro>
   */
b7f080cfe   Alexey Dobriyan   net: remove mm.h ...
11
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
  #include <linux/skbuff.h>
78776d3f2   David S. Miller   sch_netem: Need t...
18
  #include <linux/vmalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/rtnetlink.h>
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
20
  #include <linux/reciprocal_div.h>
aec0a40a6   Eric Dumazet   netem: use rb tre...
21
  #include <linux/rbtree.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

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

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  /*	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...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  
       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
64
  */
0a9fe5c37   Yousuk Seung   netem: slotting w...
65
66
  struct disttable {
  	u32  size;
b90feaff2   Gustavo A. R. Silva   net: sched: Repla...
67
  	s16 table[];
0a9fe5c37   Yousuk Seung   netem: slotting w...
68
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  struct netem_sched_data {
aec0a40a6   Eric Dumazet   netem: use rb tre...
70
71
  	/* internal t(ime)fifo qdisc uses t_root and sch->limit */
  	struct rb_root t_root;
50612537e   Eric Dumazet   netem: fix classf...
72

d66280b12   Peter Oskolkov   net: netem: use a...
73
74
75
  	/* a linear queue; reduces rbtree rebalancing when jitter is low */
  	struct sk_buff	*t_head;
  	struct sk_buff	*t_tail;
50612537e   Eric Dumazet   netem: fix classf...
76
  	/* optional qdisc for classful handling (NULL at netem init) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
  	struct Qdisc	*qdisc;
50612537e   Eric Dumazet   netem: fix classf...
78

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

112f9cb65   Dave Taht   netem: convert to...
81
82
  	s64 latency;
  	s64 jitter;
b407621c3   Stephen Hemminger   [NETEM]: use bett...
83

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  	u32 loss;
e4ae004b8   Eric Dumazet   netem: add ECN ca...
85
  	u32 ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
  	u32 limit;
  	u32 counter;
  	u32 gap;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  	u32 duplicate;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
90
  	u32 reorder;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
91
  	u32 corrupt;
6a031f67c   Yang Yingliang   sch_netem: suppor...
92
  	u64 rate;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
93
94
  	s32 packet_overhead;
  	u32 cell_size;
809fa972f   Hannes Frederic Sowa   reciprocal_divide...
95
  	struct reciprocal_value cell_size_reciprocal;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
96
  	s32 cell_overhead;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
  
  	struct crndstate {
b407621c3   Stephen Hemminger   [NETEM]: use bett...
99
100
  		u32 last;
  		u32 rho;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
101
  	} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102

0a9fe5c37   Yousuk Seung   netem: slotting w...
103
  	struct disttable *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;
836af83b5   Dave Taht   netem: support de...
132
133
134
135
136
137
  	struct tc_netem_slot slot_config;
  	struct slotstate {
  		u64 slot_next;
  		s32 packets_left;
  		s32 bytes_left;
  	} slot;
0a9fe5c37   Yousuk Seung   netem: slotting w...
138
  	struct disttable *slot_dist;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  };
50612537e   Eric Dumazet   netem: fix classf...
140
141
  /* 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...
142
143
144
145
   *
   * 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...
146
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  struct netem_skb_cb {
112f9cb65   Dave Taht   netem: convert to...
148
  	u64	        time_to_send;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  };
5f86173bd   Jussi Kivilinna   net_sched: Add qd...
150
151
  static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb)
  {
aec0a40a6   Eric Dumazet   netem: use rb tre...
152
  	/* we assume we can use skb next/prev/tstamp as storage for rb_node */
16bda13d9   David S. Miller   net: Make qdisc_s...
153
  	qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb));
175f9c1bb   Jussi Kivilinna   net_sched: Add si...
154
  	return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data;
5f86173bd   Jussi Kivilinna   net_sched: Add qd...
155
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
  /* 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...
162
  	state->last = prandom_u32();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
167
168
  }
  
  /* 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...
169
  static u32 get_crandom(struct crndstate *state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
172
  {
  	u64 value, rho;
  	unsigned long answer;
0a9fe5c37   Yousuk Seung   netem: slotting w...
173
  	if (!state || state->rho == 0)	/* no correlation */
63862b5be   Aruna-Hewapathirane   net: replace macr...
174
  		return prandom_u32();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175

63862b5be   Aruna-Hewapathirane   net: replace macr...
176
  	value = prandom_u32();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
180
181
  	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...
182
183
184
185
186
187
188
  /* 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...
189
  	u32 rnd = prandom_u32();
661b79725   stephen hemminger   netem: revised co...
190
191
  
  	/*
25985edce   Lucas De Marchi   Fix common misspe...
192
  	 * Makes a comparison between rnd and the transition
661b79725   stephen hemminger   netem: revised co...
193
194
195
  	 * 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...
196
197
198
199
  	 *   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...
200
201
  	 */
  	switch (clg->state) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
202
  	case TX_IN_GAP_PERIOD:
661b79725   stephen hemminger   netem: revised co...
203
  		if (rnd < clg->a4) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
204
  			clg->state = LOST_IN_BURST_PERIOD;
661b79725   stephen hemminger   netem: revised co...
205
  			return true;
ab6c27be8   stephen hemminger   netem: fix loss 4...
206
  		} else if (clg->a4 < rnd && rnd < clg->a1 + clg->a4) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
207
  			clg->state = LOST_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
208
  			return true;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
209
210
211
  		} else if (clg->a1 + clg->a4 < rnd) {
  			clg->state = TX_IN_GAP_PERIOD;
  		}
661b79725   stephen hemminger   netem: revised co...
212
213
  
  		break;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
214
  	case TX_IN_BURST_PERIOD:
661b79725   stephen hemminger   netem: revised co...
215
  		if (rnd < clg->a5) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
216
  			clg->state = LOST_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
217
  			return true;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
218
219
220
  		} else {
  			clg->state = TX_IN_BURST_PERIOD;
  		}
661b79725   stephen hemminger   netem: revised co...
221
222
  
  		break;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
223
  	case LOST_IN_GAP_PERIOD:
661b79725   stephen hemminger   netem: revised co...
224
  		if (rnd < clg->a3)
a6e2fe17e   Yang Yingliang   sch_netem: replac...
225
  			clg->state = TX_IN_BURST_PERIOD;
661b79725   stephen hemminger   netem: revised co...
226
  		else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
227
  			clg->state = TX_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
228
  		} else if (clg->a2 + clg->a3 < rnd) {
a6e2fe17e   Yang Yingliang   sch_netem: replac...
229
  			clg->state = LOST_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
230
231
232
  			return true;
  		}
  		break;
a6e2fe17e   Yang Yingliang   sch_netem: replac...
233
234
  	case LOST_IN_BURST_PERIOD:
  		clg->state = TX_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
235
236
237
238
239
240
241
242
243
244
  		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...
245
   * Makes a comparison between random number and the transition
661b79725   stephen hemminger   netem: revised co...
246
   * probabilities outgoing from the current state, then decides the
25985edce   Lucas De Marchi   Fix common misspe...
247
   * next state. A second random number is extracted and the comparison
661b79725   stephen hemminger   netem: revised co...
248
249
250
251
252
253
254
255
   * 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...
256
  	case GOOD_STATE:
63862b5be   Aruna-Hewapathirane   net: replace macr...
257
  		if (prandom_u32() < clg->a1)
c045a734d   Yang Yingliang   sch_netem: replac...
258
  			clg->state = BAD_STATE;
63862b5be   Aruna-Hewapathirane   net: replace macr...
259
  		if (prandom_u32() < clg->a4)
661b79725   stephen hemminger   netem: revised co...
260
  			return true;
7c2781fa9   stephen hemminger   netem: missing br...
261
  		break;
c045a734d   Yang Yingliang   sch_netem: replac...
262
  	case BAD_STATE:
63862b5be   Aruna-Hewapathirane   net: replace macr...
263
  		if (prandom_u32() < clg->a2)
c045a734d   Yang Yingliang   sch_netem: replac...
264
  			clg->state = GOOD_STATE;
63862b5be   Aruna-Hewapathirane   net: replace macr...
265
  		if (prandom_u32() > clg->a3)
661b79725   stephen hemminger   netem: revised co...
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
292
293
294
295
296
297
  			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
298
299
300
301
  /* 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.
   */
9b0ed8917   Stephen Hemminger   netem: remove unn...
302
  static s64 tabledist(s64 mu, s32 sigma,
112f9cb65   Dave Taht   netem: convert to...
303
  		     struct crndstate *state,
9b0ed8917   Stephen Hemminger   netem: remove unn...
304
  		     const struct disttable *dist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  {
112f9cb65   Dave Taht   netem: convert to...
306
  	s64 x;
b407621c3   Stephen Hemminger   [NETEM]: use bett...
307
308
  	long t;
  	u32 rnd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
313
314
315
  
  	if (sigma == 0)
  		return mu;
  
  	rnd = get_crandom(state);
  
  	/* default uniform distribution */
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
316
  	if (dist == NULL)
eadd1befd   Aleksandr Nogikh   netem: fix zero d...
317
  		return ((rnd % (2 * (u32)sigma)) + mu) - sigma;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
324
325
326
327
  
  	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;
  }
bce552fd6   Stephen Hemminger   netem: use 64 bit...
328
  static u64 packet_time_ns(u64 len, const struct netem_sched_data *q)
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
329
  {
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
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);
  	}
bce552fd6   Stephen Hemminger   netem: use 64 bit...
339
340
  
  	return div64_u64(len * NSEC_PER_SEC, q->rate);
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
341
  }
ff704050f   stephen hemminger   netem: free skb's...
342
343
344
  static void tfifo_reset(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
3aa605f28   Eric Dumazet   sch_netem: faster...
345
  	struct rb_node *p = rb_first(&q->t_root);
ff704050f   stephen hemminger   netem: free skb's...
346

3aa605f28   Eric Dumazet   sch_netem: faster...
347
  	while (p) {
18a4c0eab   Eric Dumazet   net: add rb_to_sk...
348
  		struct sk_buff *skb = rb_to_skb(p);
ff704050f   stephen hemminger   netem: free skb's...
349

3aa605f28   Eric Dumazet   sch_netem: faster...
350
351
  		p = rb_next(p);
  		rb_erase(&skb->rbnode, &q->t_root);
2f08a9a16   Eric Dumazet   net_sched: sch_ne...
352
  		rtnl_kfree_skbs(skb, skb);
ff704050f   stephen hemminger   netem: free skb's...
353
  	}
d66280b12   Peter Oskolkov   net: netem: use a...
354
355
356
357
  
  	rtnl_kfree_skbs(q->t_head, q->t_tail);
  	q->t_head = NULL;
  	q->t_tail = NULL;
ff704050f   stephen hemminger   netem: free skb's...
358
  }
960fb66e5   Eric Dumazet   netem: add limita...
359
  static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
50612537e   Eric Dumazet   netem: fix classf...
360
  {
aec0a40a6   Eric Dumazet   netem: use rb tre...
361
  	struct netem_sched_data *q = qdisc_priv(sch);
112f9cb65   Dave Taht   netem: convert to...
362
  	u64 tnext = netem_skb_cb(nskb)->time_to_send;
50612537e   Eric Dumazet   netem: fix classf...
363

d66280b12   Peter Oskolkov   net: netem: use a...
364
365
366
  	if (!q->t_tail || tnext >= netem_skb_cb(q->t_tail)->time_to_send) {
  		if (q->t_tail)
  			q->t_tail->next = nskb;
aec0a40a6   Eric Dumazet   netem: use rb tre...
367
  		else
d66280b12   Peter Oskolkov   net: netem: use a...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
  			q->t_head = nskb;
  		q->t_tail = nskb;
  	} else {
  		struct rb_node **p = &q->t_root.rb_node, *parent = NULL;
  
  		while (*p) {
  			struct sk_buff *skb;
  
  			parent = *p;
  			skb = rb_to_skb(parent);
  			if (tnext >= netem_skb_cb(skb)->time_to_send)
  				p = &parent->rb_right;
  			else
  				p = &parent->rb_left;
  		}
  		rb_link_node(&nskb->rbnode, parent, p);
  		rb_insert_color(&nskb->rbnode, &q->t_root);
50612537e   Eric Dumazet   netem: fix classf...
385
  	}
aec0a40a6   Eric Dumazet   netem: use rb tre...
386
  	sch->q.qlen++;
50612537e   Eric Dumazet   netem: fix classf...
387
  }
6071bd1aa   Neil Horman   netem: Segment GS...
388
389
390
391
  /* 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...
392
393
  static struct sk_buff *netem_segment(struct sk_buff *skb, struct Qdisc *sch,
  				     struct sk_buff **to_free)
6071bd1aa   Neil Horman   netem: Segment GS...
394
395
396
397
398
399
400
  {
  	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...
401
  		qdisc_drop(skb, sch, to_free);
6071bd1aa   Neil Horman   netem: Segment GS...
402
403
404
405
406
  		return NULL;
  	}
  	consume_skb(skb);
  	return segs;
  }
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
407
408
409
410
411
412
  /*
   * 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...
413
414
  static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
  			 struct sk_buff **to_free)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
89e1df74f   Guillaume Chazarain   [PKT_SCHED] netem...
417
418
  	/* We don't fill cb now as skb_unshare() may invalidate it */
  	struct netem_skb_cb *cb;
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
419
  	struct sk_buff *skb2;
6071bd1aa   Neil Horman   netem: Segment GS...
420
  	struct sk_buff *segs = NULL;
177b80074   Jakub Kicinski   net: netem: fix b...
421
  	unsigned int prev_len = qdisc_pkt_len(skb);
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
422
  	int count = 1;
6071bd1aa   Neil Horman   netem: Segment GS...
423
  	int rc = NET_XMIT_SUCCESS;
5845f7063   Sheng Lan   net: netem: fix s...
424
  	int rc_drop = NET_XMIT_DROP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425

9410d386d   Christoph Paasch   net: Prevent inva...
426
427
  	/* Do not fool qdisc_drop_all() */
  	skb->prev = NULL;
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
428
429
430
  	/* Random duplication */
  	if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
  		++count;
661b79725   stephen hemminger   netem: revised co...
431
  	/* Drop packet? */
e4ae004b8   Eric Dumazet   netem: add ECN ca...
432
433
  	if (loss_event(q)) {
  		if (q->ecn && INET_ECN_set_ce(skb))
25331d6ce   John Fastabend   net: sched: imple...
434
  			qdisc_qstats_drop(sch); /* mark packet */
e4ae004b8   Eric Dumazet   netem: add ECN ca...
435
436
437
  		else
  			--count;
  	}
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
438
  	if (count == 0) {
25331d6ce   John Fastabend   net: sched: imple...
439
  		qdisc_qstats_drop(sch);
520ac30f4   Eric Dumazet   net_sched: drop p...
440
  		__qdisc_drop(skb, to_free);
c27f339af   Jarek Poplawski   net_sched: Add qd...
441
  		return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  	}
5a308f40b   Eric Dumazet   netem: refine ear...
443
444
  	/* 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...
445
  	 */
5080f39e8   Nik Unger   netem: apply corr...
446
  	if (q->latency || q->jitter || q->rate)
f2f872f92   Eric Dumazet   netem: Introduce ...
447
  		skb_orphan_partial(skb);
4e8a52015   David S. Miller   [PKT_SCHED] netem...
448

0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
449
450
451
452
453
454
  	/*
  	 * 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) {
159d2c7d8   Eric Dumazet   sch_netem: fix rc...
455
  		struct Qdisc *rootq = qdisc_root_bh(sch);
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
456
  		u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
457

b396cca6f   Eric Dumazet   net: sched: depre...
458
  		q->duplicate = 0;
520ac30f4   Eric Dumazet   net_sched: drop p...
459
  		rootq->enqueue(skb2, rootq, to_free);
0afb51e72   Stephen Hemminger   [PKT_SCHED]: nete...
460
  		q->duplicate = dupsave;
5845f7063   Sheng Lan   net: netem: fix s...
461
  		rc_drop = NET_XMIT_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
  	}
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
463
464
465
466
467
468
469
  	/*
  	 * 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...
470
  		if (skb_is_gso(skb)) {
3e14c383d   Jakub Kicinski   net: netem: fix u...
471
472
  			skb = netem_segment(skb, sch, to_free);
  			if (!skb)
5845f7063   Sheng Lan   net: netem: fix s...
473
  				return rc_drop;
3e14c383d   Jakub Kicinski   net: netem: fix u...
474
475
476
  			segs = skb->next;
  			skb_mark_not_on_list(skb);
  			qdisc_skb_cb(skb)->pkt_len = skb->len;
6071bd1aa   Neil Horman   netem: Segment GS...
477
  		}
8a6e9c670   Eric Dumazet   net_sched: netem:...
478
479
480
481
482
483
484
485
  		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);
a7fa12d15   Jakub Kicinski   net: netem: fix e...
486
  			skb = NULL;
6071bd1aa   Neil Horman   netem: Segment GS...
487
488
  			goto finish_segs;
  		}
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
489

63862b5be   Aruna-Hewapathirane   net: replace macr...
490
491
  		skb->data[prandom_u32() % skb_headlen(skb)] ^=
  			1<<(prandom_u32() % 8);
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
492
  	}
5845f7063   Sheng Lan   net: netem: fix s...
493
  	if (unlikely(sch->q.qlen >= sch->limit)) {
3e14c383d   Jakub Kicinski   net: netem: fix u...
494
495
  		/* re-link segs, so that qdisc_drop_all() frees them all */
  		skb->next = segs;
5845f7063   Sheng Lan   net: netem: fix s...
496
497
498
  		qdisc_drop_all(skb, sch, to_free);
  		return rc_drop;
  	}
960fb66e5   Eric Dumazet   netem: add limita...
499

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

5f86173bd   Jussi Kivilinna   net_sched: Add qd...
502
  	cb = netem_skb_cb(skb);
cc7ec456f   Eric Dumazet   net_sched: cleanups
503
  	if (q->gap == 0 ||		/* not doing reordering */
a42b4799c   Vijay Subramanian   netem: Fix off-by...
504
  	    q->counter < q->gap - 1 ||	/* inside last reordering gap */
f64f9e719   Joe Perches   net: Move && and ...
505
  	    q->reorder < get_crandom(&q->reorder_cor)) {
112f9cb65   Dave Taht   netem: convert to...
506
507
  		u64 now;
  		s64 delay;
07aaa1154   Stephen Hemminger   [NETEM]: use PSCH...
508
509
510
  
  		delay = tabledist(q->latency, q->jitter,
  				  &q->delay_cor, q->delay_dist);
112f9cb65   Dave Taht   netem: convert to...
511
  		now = ktime_get_ns();
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
512
513
  
  		if (q->rate) {
5080f39e8   Nik Unger   netem: apply corr...
514
515
516
517
518
519
520
  			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;
18a4c0eab   Eric Dumazet   net: add rb_to_sk...
521
  				t_skb = skb_rb_last(&q->t_root);
5080f39e8   Nik Unger   netem: apply corr...
522
523
  				t_last = netem_skb_cb(t_skb);
  				if (!last ||
d66280b12   Peter Oskolkov   net: netem: use a...
524
525
526
527
528
529
530
531
532
  				    t_last->time_to_send > last->time_to_send)
  					last = t_last;
  			}
  			if (q->t_tail) {
  				struct netem_skb_cb *t_last =
  					netem_skb_cb(q->t_tail);
  
  				if (!last ||
  				    t_last->time_to_send > last->time_to_send)
5080f39e8   Nik Unger   netem: apply corr...
533
  					last = t_last;
5080f39e8   Nik Unger   netem: apply corr...
534
  			}
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
535

aec0a40a6   Eric Dumazet   netem: use rb tre...
536
  			if (last) {
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
537
  				/*
a13d31047   Johannes Naab   netem: fix delay ...
538
539
  				 * Last packet in queue is reference point (now),
  				 * calculate this time bonus and subtract
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
540
541
  				 * from delay.
  				 */
5080f39e8   Nik Unger   netem: apply corr...
542
  				delay -= last->time_to_send - now;
112f9cb65   Dave Taht   netem: convert to...
543
  				delay = max_t(s64, 0, delay);
5080f39e8   Nik Unger   netem: apply corr...
544
  				now = last->time_to_send;
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
545
  			}
a13d31047   Johannes Naab   netem: fix delay ...
546

bce552fd6   Stephen Hemminger   netem: use 64 bit...
547
  			delay += packet_time_ns(qdisc_pkt_len(skb), q);
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
548
  		}
7c59e25f3   Patrick McHardy   [NET_SCHED]: kill...
549
  		cb->time_to_send = now + delay;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
  		++q->counter;
960fb66e5   Eric Dumazet   netem: add limita...
551
  		tfifo_enqueue(skb, sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
  	} else {
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
553
  		/*
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
554
555
556
  		 * Do re-ordering by putting one out of N packets at the front
  		 * of the queue.
  		 */
112f9cb65   Dave Taht   netem: convert to...
557
  		cb->time_to_send = ktime_get_ns();
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
558
  		q->counter = 0;
8ba25dad0   Jarek Poplawski   sch_netem: Replac...
559

596977300   David S. Miller   sch_netem: Move p...
560
  		__qdisc_enqueue_head(skb, &sch->q);
eb1019244   Hagen Paul Pfeifer   net_sched: Bug in...
561
  		sch->qstats.requeues++;
378a2f090   Jarek Poplawski   net_sched: Add qd...
562
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563

6071bd1aa   Neil Horman   netem: Segment GS...
564
565
  finish_segs:
  	if (segs) {
177b80074   Jakub Kicinski   net: netem: fix b...
566
  		unsigned int len, last_len;
a7fa12d15   Jakub Kicinski   net: netem: fix e...
567
  		int nb;
177b80074   Jakub Kicinski   net: netem: fix b...
568

a7fa12d15   Jakub Kicinski   net: netem: fix e...
569
570
  		len = skb ? skb->len : 0;
  		nb = skb ? 1 : 0;
177b80074   Jakub Kicinski   net: netem: fix b...
571

6071bd1aa   Neil Horman   netem: Segment GS...
572
573
  		while (segs) {
  			skb2 = segs->next;
a8305bff6   David S. Miller   net: Add and use ...
574
  			skb_mark_not_on_list(segs);
6071bd1aa   Neil Horman   netem: Segment GS...
575
576
  			qdisc_skb_cb(segs)->pkt_len = segs->len;
  			last_len = segs->len;
520ac30f4   Eric Dumazet   net_sched: drop p...
577
  			rc = qdisc_enqueue(segs, sch, to_free);
6071bd1aa   Neil Horman   netem: Segment GS...
578
579
580
581
582
583
584
585
586
  			if (rc != NET_XMIT_SUCCESS) {
  				if (net_xmit_drop_count(rc))
  					qdisc_qstats_drop(sch);
  			} else {
  				nb++;
  				len += last_len;
  			}
  			segs = skb2;
  		}
a7fa12d15   Jakub Kicinski   net: netem: fix e...
587
588
  		/* Parent qdiscs accounted for 1 skb of size @prev_len */
  		qdisc_tree_reduce_backlog(sch, -(nb - 1), -(len - prev_len));
e0ad032e1   Jakub Kicinski   net: netem: corre...
589
590
  	} else if (!skb) {
  		return NET_XMIT_DROP;
6071bd1aa   Neil Horman   netem: Segment GS...
591
  	}
10f6dfcfd   stephen hemminger   Revert "sch_netem...
592
  	return NET_XMIT_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  }
836af83b5   Dave Taht   netem: support de...
594
595
596
597
598
599
  /* Delay the next round with a new future slot with a
   * correct number of bytes and packets.
   */
  
  static void get_slot_next(struct netem_sched_data *q, u64 now)
  {
0a9fe5c37   Yousuk Seung   netem: slotting w...
600
601
602
603
604
605
606
607
608
609
610
611
612
  	s64 next_delay;
  
  	if (!q->slot_dist)
  		next_delay = q->slot_config.min_delay +
  				(prandom_u32() *
  				 (q->slot_config.max_delay -
  				  q->slot_config.min_delay) >> 32);
  	else
  		next_delay = tabledist(q->slot_config.dist_delay,
  				       (s32)(q->slot_config.dist_jitter),
  				       NULL, q->slot_dist);
  
  	q->slot.slot_next = now + next_delay;
836af83b5   Dave Taht   netem: support de...
613
614
615
  	q->slot.packets_left = q->slot_config.max_packets;
  	q->slot.bytes_left = q->slot_config.max_bytes;
  }
d66280b12   Peter Oskolkov   net: netem: use a...
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
  static struct sk_buff *netem_peek(struct netem_sched_data *q)
  {
  	struct sk_buff *skb = skb_rb_first(&q->t_root);
  	u64 t1, t2;
  
  	if (!skb)
  		return q->t_head;
  	if (!q->t_head)
  		return skb;
  
  	t1 = netem_skb_cb(skb)->time_to_send;
  	t2 = netem_skb_cb(q->t_head)->time_to_send;
  	if (t1 < t2)
  		return skb;
  	return q->t_head;
  }
  
  static void netem_erase_head(struct netem_sched_data *q, struct sk_buff *skb)
  {
  	if (skb == q->t_head) {
  		q->t_head = skb->next;
  		if (!q->t_head)
  			q->t_tail = NULL;
  	} else {
  		rb_erase(&skb->rbnode, &q->t_root);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
644
645
646
  static struct sk_buff *netem_dequeue(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
  	struct sk_buff *skb;
50612537e   Eric Dumazet   netem: fix classf...
647
  tfifo_dequeue:
ed760cb8a   Florian Westphal   sched: replace __...
648
  	skb = __qdisc_dequeue_head(&sch->q);
771018e76   Stephen Hemminger   [PKT_SCHED]: nete...
649
  	if (skb) {
25331d6ce   John Fastabend   net: sched: imple...
650
  		qdisc_qstats_backlog_dec(sch, skb);
0ad2a8365   Beshay, Joseph   netem: Fixes byte...
651
  deliver:
aec0a40a6   Eric Dumazet   netem: use rb tre...
652
653
654
  		qdisc_bstats_update(sch, skb);
  		return skb;
  	}
d66280b12   Peter Oskolkov   net: netem: use a...
655
656
  	skb = netem_peek(q);
  	if (skb) {
112f9cb65   Dave Taht   netem: convert to...
657
  		u64 time_to_send;
836af83b5   Dave Taht   netem: support de...
658
  		u64 now = ktime_get_ns();
36b7bfe09   Eric Dumazet   netem: fix possib...
659

0f9f32ac6   Stephen Hemminger   [PKT_SCHED] netem...
660
  		/* if more time remaining? */
36b7bfe09   Eric Dumazet   netem: fix possib...
661
  		time_to_send = netem_skb_cb(skb)->time_to_send;
836af83b5   Dave Taht   netem: support de...
662
663
  		if (q->slot.slot_next && q->slot.slot_next < time_to_send)
  			get_slot_next(q, now);
aec0a40a6   Eric Dumazet   netem: use rb tre...
664

d66280b12   Peter Oskolkov   net: netem: use a...
665
666
  		if (time_to_send <= now && q->slot.slot_next <= now) {
  			netem_erase_head(q, skb);
aec0a40a6   Eric Dumazet   netem: use rb tre...
667
  			sch->q.qlen--;
0ad2a8365   Beshay, Joseph   netem: Fixes byte...
668
  			qdisc_qstats_backlog_dec(sch, skb);
aec0a40a6   Eric Dumazet   netem: use rb tre...
669
670
  			skb->next = NULL;
  			skb->prev = NULL;
bffa72cf7   Eric Dumazet   net: sk_buff rbno...
671
672
673
674
  			/* skb->dev shares skb->rbnode area,
  			 * we need to restore its value.
  			 */
  			skb->dev = qdisc_dev(sch);
03c05f0d4   Jarek Poplawski   pkt_sched: Use qd...
675

836af83b5   Dave Taht   netem: support de...
676
677
678
679
680
681
682
  			if (q->slot.slot_next) {
  				q->slot.packets_left--;
  				q->slot.bytes_left -= qdisc_pkt_len(skb);
  				if (q->slot.packets_left <= 0 ||
  				    q->slot.bytes_left <= 0)
  					get_slot_next(q, now);
  			}
50612537e   Eric Dumazet   netem: fix classf...
683
  			if (q->qdisc) {
21de12ee5   Eric Dumazet   netem: fix a use ...
684
  				unsigned int pkt_len = qdisc_pkt_len(skb);
520ac30f4   Eric Dumazet   net_sched: drop p...
685
686
  				struct sk_buff *to_free = NULL;
  				int err;
50612537e   Eric Dumazet   netem: fix classf...
687

520ac30f4   Eric Dumazet   net_sched: drop p...
688
689
  				err = qdisc_enqueue(skb, q->qdisc, &to_free);
  				kfree_skb_list(to_free);
21de12ee5   Eric Dumazet   netem: fix a use ...
690
691
692
693
694
  				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...
695
696
697
  				}
  				goto tfifo_dequeue;
  			}
aec0a40a6   Eric Dumazet   netem: use rb tre...
698
  			goto deliver;
07aaa1154   Stephen Hemminger   [NETEM]: use PSCH...
699
  		}
11274e5a4   Stephen Hemminger   [NETEM]: avoid ex...
700

50612537e   Eric Dumazet   netem: fix classf...
701
702
703
704
705
  		if (q->qdisc) {
  			skb = q->qdisc->ops->dequeue(q->qdisc);
  			if (skb)
  				goto deliver;
  		}
836af83b5   Dave Taht   netem: support de...
706
707
708
709
  
  		qdisc_watchdog_schedule_ns(&q->watchdog,
  					   max(time_to_send,
  					       q->slot.slot_next));
0f9f32ac6   Stephen Hemminger   [PKT_SCHED] netem...
710
  	}
50612537e   Eric Dumazet   netem: fix classf...
711
712
713
714
715
  	if (q->qdisc) {
  		skb = q->qdisc->ops->dequeue(q->qdisc);
  		if (skb)
  			goto deliver;
  	}
0f9f32ac6   Stephen Hemminger   [PKT_SCHED] netem...
716
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
719
720
  static void netem_reset(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
50612537e   Eric Dumazet   netem: fix classf...
721
  	qdisc_reset_queue(sch);
ff704050f   stephen hemminger   netem: free skb's...
722
  	tfifo_reset(sch);
50612537e   Eric Dumazet   netem: fix classf...
723
724
  	if (q->qdisc)
  		qdisc_reset(q->qdisc);
59cb5c673   Patrick McHardy   [NET_SCHED]: sch_...
725
  	qdisc_watchdog_cancel(&q->watchdog);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  }
6373a9a28   stephen hemminger   netem: use vmallo...
727
728
  static void dist_free(struct disttable *d)
  {
4cb28970a   WANG Cong   net: use the new ...
729
  	kvfree(d);
6373a9a28   stephen hemminger   netem: use vmallo...
730
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
733
734
  /*
   * Distribution data is a variable size payload containing
   * signed 16 bit values.
   */
836af83b5   Dave Taht   netem: support de...
735

0a9fe5c37   Yousuk Seung   netem: slotting w...
736
737
  static int get_dist_table(struct Qdisc *sch, struct disttable **tbl,
  			  const struct nlattr *attr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
  {
6373a9a28   stephen hemminger   netem: use vmallo...
739
  	size_t n = nla_len(attr)/sizeof(__s16);
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
740
  	const __s16 *data = nla_data(attr);
7698b4fca   David S. Miller   pkt_sched: Add an...
741
  	spinlock_t *root_lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
  	struct disttable *d;
  	int i;
b41d936b5   Eric Dumazet   sch_netem: fix a ...
744
  	if (!n || n > NETEM_DIST_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
  		return -EINVAL;
752ade68c   Michal Hocko   treewide: use kv[...
746
  	d = kvmalloc(sizeof(struct disttable) + n * sizeof(s16), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
751
752
  	if (!d)
  		return -ENOMEM;
  
  	d->size = n;
  	for (i = 0; i < n; i++)
  		d->table[i] = data[i];
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
753

102396ae6   Jarek Poplawski   pkt_sched: Fix lo...
754
  	root_lock = qdisc_root_sleeping_lock(sch);
7698b4fca   David S. Miller   pkt_sched: Add an...
755
756
  
  	spin_lock_bh(root_lock);
0a9fe5c37   Yousuk Seung   netem: slotting w...
757
  	swap(*tbl, d);
7698b4fca   David S. Miller   pkt_sched: Add an...
758
  	spin_unlock_bh(root_lock);
bb52c7acf   Eric Dumazet   netem: dont call ...
759
760
  
  	dist_free(d);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
  	return 0;
  }
836af83b5   Dave Taht   netem: support de...
763
764
765
766
767
768
769
770
771
  static void get_slot(struct netem_sched_data *q, const struct nlattr *attr)
  {
  	const struct tc_netem_slot *c = nla_data(attr);
  
  	q->slot_config = *c;
  	if (q->slot_config.max_packets == 0)
  		q->slot_config.max_packets = INT_MAX;
  	if (q->slot_config.max_bytes == 0)
  		q->slot_config.max_bytes = INT_MAX;
eadd1befd   Aleksandr Nogikh   netem: fix zero d...
772
773
774
  
  	/* capping dist_jitter to the range acceptable by tabledist() */
  	q->slot_config.dist_jitter = min_t(__s64, INT_MAX, abs(q->slot_config.dist_jitter));
836af83b5   Dave Taht   netem: support de...
775
776
  	q->slot.packets_left = q->slot_config.max_packets;
  	q->slot.bytes_left = q->slot_config.max_bytes;
0a9fe5c37   Yousuk Seung   netem: slotting w...
777
778
  	if (q->slot_config.min_delay | q->slot_config.max_delay |
  	    q->slot_config.dist_jitter)
836af83b5   Dave Taht   netem: support de...
779
780
781
782
  		q->slot.slot_next = ktime_get_ns();
  	else
  		q->slot.slot_next = 0;
  }
49545a777   Yang Yingliang   sch_netem: change...
783
  static void get_correlation(struct netem_sched_data *q, const struct nlattr *attr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
  {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
785
  	const struct tc_netem_corr *c = nla_data(attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
788
789
  	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
790
  }
49545a777   Yang Yingliang   sch_netem: change...
791
  static void get_reorder(struct netem_sched_data *q, const struct nlattr *attr)
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
792
  {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
793
  	const struct tc_netem_reorder *r = nla_data(attr);
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
794

0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
795
796
  	q->reorder = r->probability;
  	init_crandom(&q->reorder_cor, r->correlation);
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
797
  }
49545a777   Yang Yingliang   sch_netem: change...
798
  static void get_corrupt(struct netem_sched_data *q, const struct nlattr *attr)
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
799
  {
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
800
  	const struct tc_netem_corrupt *r = nla_data(attr);
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
801

c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
802
803
  	q->corrupt = r->probability;
  	init_crandom(&q->corrupt_cor, r->correlation);
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
804
  }
49545a777   Yang Yingliang   sch_netem: change...
805
  static void get_rate(struct netem_sched_data *q, const struct nlattr *attr)
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
806
  {
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
807
808
809
  	const struct tc_netem_rate *r = nla_data(attr);
  
  	q->rate = r->rate;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
810
811
  	q->packet_overhead = r->packet_overhead;
  	q->cell_size = r->cell_size;
809fa972f   Hannes Frederic Sowa   reciprocal_divide...
812
  	q->cell_overhead = r->cell_overhead;
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
813
814
  	if (q->cell_size)
  		q->cell_size_reciprocal = reciprocal_value(q->cell_size);
809fa972f   Hannes Frederic Sowa   reciprocal_divide...
815
816
  	else
  		q->cell_size_reciprocal = (struct reciprocal_value) { 0 };
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
817
  }
49545a777   Yang Yingliang   sch_netem: change...
818
  static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr)
661b79725   stephen hemminger   netem: revised co...
819
  {
661b79725   stephen hemminger   netem: revised co...
820
821
822
823
824
  	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...
825
  		switch (type) {
661b79725   stephen hemminger   netem: revised co...
826
827
  		case NETEM_LOSS_GI: {
  			const struct tc_netem_gimodel *gi = nla_data(la);
2494654d4   stephen hemminger   netem: loss model...
828
  			if (nla_len(la) < sizeof(struct tc_netem_gimodel)) {
661b79725   stephen hemminger   netem: revised co...
829
830
831
832
833
834
  				pr_info("netem: incorrect gi model size
  ");
  				return -EINVAL;
  			}
  
  			q->loss_model = CLG_4_STATES;
3fbac2a87   Yang Yingliang   sch_netem: replac...
835
  			q->clg.state = TX_IN_GAP_PERIOD;
661b79725   stephen hemminger   netem: revised co...
836
837
838
839
840
841
842
843
844
845
  			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...
846
847
848
  			if (nla_len(la) < sizeof(struct tc_netem_gemodel)) {
  				pr_info("netem: incorrect ge model size
  ");
661b79725   stephen hemminger   netem: revised co...
849
850
851
852
  				return -EINVAL;
  			}
  
  			q->loss_model = CLG_GILB_ELL;
3fbac2a87   Yang Yingliang   sch_netem: replac...
853
  			q->clg.state = GOOD_STATE;
661b79725   stephen hemminger   netem: revised co...
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
  			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 ...
870
871
872
873
  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...
874
  	[TCA_NETEM_RATE]	= { .len = sizeof(struct tc_netem_rate) },
661b79725   stephen hemminger   netem: revised co...
875
  	[TCA_NETEM_LOSS]	= { .type = NLA_NESTED },
e4ae004b8   Eric Dumazet   netem: add ECN ca...
876
  	[TCA_NETEM_ECN]		= { .type = NLA_U32 },
6a031f67c   Yang Yingliang   sch_netem: suppor...
877
  	[TCA_NETEM_RATE64]	= { .type = NLA_U64 },
99803171e   Dave Taht   netem: add uapi t...
878
879
  	[TCA_NETEM_LATENCY64]	= { .type = NLA_S64 },
  	[TCA_NETEM_JITTER64]	= { .type = NLA_S64 },
836af83b5   Dave Taht   netem: support de...
880
  	[TCA_NETEM_SLOT]	= { .len = sizeof(struct tc_netem_slot) },
27a3421e4   Patrick McHardy   [NET_SCHED]: Use ...
881
  };
2c10b32bf   Thomas Graf   netlink: Remove c...
882
883
884
885
  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...
886
887
888
  	if (nested_len < 0) {
  		pr_info("netem: invalid attributes len %d
  ", nested_len);
2c10b32bf   Thomas Graf   netlink: Remove c...
889
  		return -EINVAL;
661b79725   stephen hemminger   netem: revised co...
890
  	}
2c10b32bf   Thomas Graf   netlink: Remove c...
891
  	if (nested_len >= nla_attr_size(0))
8cb081746   Johannes Berg   netlink: make val...
892
893
894
  		return nla_parse_deprecated(tb, maxtype,
  					    nla_data(nla) + NLA_ALIGN(len),
  					    nested_len, policy, NULL);
661b79725   stephen hemminger   netem: revised co...
895

2c10b32bf   Thomas Graf   netlink: Remove c...
896
897
898
  	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
  	return 0;
  }
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
899
  /* Parse netlink message to set options */
2030721cc   Alexander Aring   net: sched: sch: ...
900
901
  static int netem_change(struct Qdisc *sch, struct nlattr *opt,
  			struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
b03f46720   Patrick McHardy   [NET_SCHED]: sch_...
904
  	struct nlattr *tb[TCA_NETEM_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
  	struct tc_netem_qopt *qopt;
54a4b05cd   Yang Yingliang   sch_netem: return...
906
907
  	struct clgstate old_clg;
  	int old_loss_model = CLG_RANDOM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
  	int ret;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
909

b03f46720   Patrick McHardy   [NET_SCHED]: sch_...
910
  	if (opt == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
  		return -EINVAL;
2c10b32bf   Thomas Graf   netlink: Remove c...
912
913
  	qopt = nla_data(opt);
  	ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt));
b03f46720   Patrick McHardy   [NET_SCHED]: sch_...
914
915
  	if (ret < 0)
  		return ret;
54a4b05cd   Yang Yingliang   sch_netem: return...
916
917
918
919
920
  	/* 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...
921
  		ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]);
54a4b05cd   Yang Yingliang   sch_netem: return...
922
923
924
925
926
927
928
929
930
  		if (ret) {
  			q->loss_model = old_loss_model;
  			return ret;
  		}
  	} else {
  		q->loss_model = CLG_RANDOM;
  	}
  
  	if (tb[TCA_NETEM_DELAY_DIST]) {
0a9fe5c37   Yousuk Seung   netem: slotting w...
931
932
933
934
935
936
937
938
939
940
941
  		ret = get_dist_table(sch, &q->delay_dist,
  				     tb[TCA_NETEM_DELAY_DIST]);
  		if (ret)
  			goto get_table_failure;
  	}
  
  	if (tb[TCA_NETEM_SLOT_DIST]) {
  		ret = get_dist_table(sch, &q->slot_dist,
  				     tb[TCA_NETEM_SLOT_DIST]);
  		if (ret)
  			goto get_table_failure;
54a4b05cd   Yang Yingliang   sch_netem: return...
942
  	}
50612537e   Eric Dumazet   netem: fix classf...
943
  	sch->limit = qopt->limit;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
944

112f9cb65   Dave Taht   netem: convert to...
945
946
  	q->latency = PSCHED_TICKS2NS(qopt->latency);
  	q->jitter = PSCHED_TICKS2NS(qopt->jitter);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
948
  	q->limit = qopt->limit;
  	q->gap = qopt->gap;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
949
  	q->counter = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950
951
  	q->loss = qopt->loss;
  	q->duplicate = qopt->duplicate;
bb2f8cc0e   Stephen Hemminger   [NETEM]: spelling...
952
953
  	/* for compatibility with earlier versions.
  	 * if gap is set, need to assume 100% probability
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
954
  	 */
a362e0a78   Stephen Hemminger   [NETEM]: report r...
955
956
  	if (q->gap)
  		q->reorder = ~0;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
957

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

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

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

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

6a031f67c   Yang Yingliang   sch_netem: suppor...
970
971
972
  	if (tb[TCA_NETEM_RATE64])
  		q->rate = max_t(u64, q->rate,
  				nla_get_u64(tb[TCA_NETEM_RATE64]));
99803171e   Dave Taht   netem: add uapi t...
973
974
975
976
977
  	if (tb[TCA_NETEM_LATENCY64])
  		q->latency = nla_get_s64(tb[TCA_NETEM_LATENCY64]);
  
  	if (tb[TCA_NETEM_JITTER64])
  		q->jitter = nla_get_s64(tb[TCA_NETEM_JITTER64]);
e4ae004b8   Eric Dumazet   netem: add ECN ca...
978
979
  	if (tb[TCA_NETEM_ECN])
  		q->ecn = nla_get_u32(tb[TCA_NETEM_ECN]);
836af83b5   Dave Taht   netem: support de...
980
981
  	if (tb[TCA_NETEM_SLOT])
  		get_slot(q, tb[TCA_NETEM_SLOT]);
eadd1befd   Aleksandr Nogikh   netem: fix zero d...
982
983
  	/* capping jitter to the range acceptable by tabledist() */
  	q->jitter = min_t(s64, abs(q->jitter), INT_MAX);
661b79725   stephen hemminger   netem: revised co...
984
  	return ret;
0a9fe5c37   Yousuk Seung   netem: slotting w...
985
986
987
988
989
990
991
992
993
  
  get_table_failure:
  	/* 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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
  }
e63d7dfd2   Alexander Aring   net: sched: sch: ...
995
996
  static int netem_init(struct Qdisc *sch, struct nlattr *opt,
  		      struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
998
999
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
  	int ret;
634576a18   Nikolay Aleksandrov   sch_netem: avoid ...
1000
  	qdisc_watchdog_init(&q->watchdog, sch);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
  	if (!opt)
  		return -EINVAL;
661b79725   stephen hemminger   netem: revised co...
1003
  	q->loss_model = CLG_RANDOM;
2030721cc   Alexander Aring   net: sched: sch: ...
1004
  	ret = netem_change(sch, opt, extack);
50612537e   Eric Dumazet   netem: fix classf...
1005
  	if (ret)
250a65f78   stephen hemminger   netem: update ver...
1006
1007
  		pr_info("netem: change failed
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
1009
1010
1011
1012
1013
  	return ret;
  }
  
  static void netem_destroy(struct Qdisc *sch)
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
59cb5c673   Patrick McHardy   [NET_SCHED]: sch_...
1014
  	qdisc_watchdog_cancel(&q->watchdog);
50612537e   Eric Dumazet   netem: fix classf...
1015
  	if (q->qdisc)
86bd446b5   Vlad Buslov   net: sched: renam...
1016
  		qdisc_put(q->qdisc);
6373a9a28   stephen hemminger   netem: use vmallo...
1017
  	dist_free(q->delay_dist);
0a9fe5c37   Yousuk Seung   netem: slotting w...
1018
  	dist_free(q->slot_dist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1019
  }
661b79725   stephen hemminger   netem: revised co...
1020
1021
1022
1023
  static int dump_loss_model(const struct netem_sched_data *q,
  			   struct sk_buff *skb)
  {
  	struct nlattr *nest;
ae0be8de9   Michal Kubecek   netlink: make nla...
1024
  	nest = nla_nest_start_noflag(skb, TCA_NETEM_LOSS);
661b79725   stephen hemminger   netem: revised co...
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  	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...
1042
1043
  		if (nla_put(skb, NETEM_LOSS_GI, sizeof(gi), &gi))
  			goto nla_put_failure;
661b79725   stephen hemminger   netem: revised co...
1044
1045
1046
1047
1048
1049
1050
1051
1052
  		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...
1053
1054
  		if (nla_put(skb, NETEM_LOSS_GE, sizeof(ge), &ge))
  			goto nla_put_failure;
661b79725   stephen hemminger   netem: revised co...
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
  		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
1066
1067
1068
  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...
1069
  	struct nlattr *nla = (struct nlattr *) skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
  	struct tc_netem_qopt qopt;
  	struct tc_netem_corr cor;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
1072
  	struct tc_netem_reorder reorder;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
1073
  	struct tc_netem_corrupt corrupt;
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
1074
  	struct tc_netem_rate rate;
836af83b5   Dave Taht   netem: support de...
1075
  	struct tc_netem_slot slot;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076

112f9cb65   Dave Taht   netem: convert to...
1077
1078
1079
1080
  	qopt.latency = min_t(psched_tdiff_t, PSCHED_NS2TICKS(q->latency),
  			     UINT_MAX);
  	qopt.jitter = min_t(psched_tdiff_t, PSCHED_NS2TICKS(q->jitter),
  			    UINT_MAX);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1081
1082
1083
1084
  	qopt.limit = q->limit;
  	qopt.loss = q->loss;
  	qopt.gap = q->gap;
  	qopt.duplicate = q->duplicate;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
1085
1086
  	if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087

99803171e   Dave Taht   netem: add uapi t...
1088
1089
1090
1091
1092
  	if (nla_put(skb, TCA_NETEM_LATENCY64, sizeof(q->latency), &q->latency))
  		goto nla_put_failure;
  
  	if (nla_put(skb, TCA_NETEM_JITTER64, sizeof(q->jitter), &q->jitter))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1093
1094
1095
  	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...
1096
1097
  	if (nla_put(skb, TCA_NETEM_CORR, sizeof(cor), &cor))
  		goto nla_put_failure;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
1098
1099
1100
  
  	reorder.probability = q->reorder;
  	reorder.correlation = q->reorder_cor.rho;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
1101
1102
  	if (nla_put(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder))
  		goto nla_put_failure;
0dca51d36   Stephen Hemminger   [PKT_SCHED] netem...
1103

c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
1104
1105
  	corrupt.probability = q->corrupt;
  	corrupt.correlation = q->corrupt_cor.rho;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
1106
1107
  	if (nla_put(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt))
  		goto nla_put_failure;
c865e5d99   Stephen Hemminger   [PKT_SCHED] netem...
1108

6a031f67c   Yang Yingliang   sch_netem: suppor...
1109
  	if (q->rate >= (1ULL << 32)) {
2a51c1e8e   Nicolas Dichtel   sched: use nla_pu...
1110
1111
  		if (nla_put_u64_64bit(skb, TCA_NETEM_RATE64, q->rate,
  				      TCA_NETEM_PAD))
6a031f67c   Yang Yingliang   sch_netem: suppor...
1112
1113
1114
1115
1116
  			goto nla_put_failure;
  		rate.rate = ~0U;
  	} else {
  		rate.rate = q->rate;
  	}
90b41a1cd   Hagen Paul Pfeifer   netem: add cell c...
1117
1118
1119
  	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...
1120
1121
  	if (nla_put(skb, TCA_NETEM_RATE, sizeof(rate), &rate))
  		goto nla_put_failure;
7bc0f28c7   Hagen Paul Pfeifer   netem: rate exten...
1122

e4ae004b8   Eric Dumazet   netem: add ECN ca...
1123
1124
  	if (q->ecn && nla_put_u32(skb, TCA_NETEM_ECN, q->ecn))
  		goto nla_put_failure;
661b79725   stephen hemminger   netem: revised co...
1125
1126
  	if (dump_loss_model(q, skb) != 0)
  		goto nla_put_failure;
0a9fe5c37   Yousuk Seung   netem: slotting w...
1127
1128
  	if (q->slot_config.min_delay | q->slot_config.max_delay |
  	    q->slot_config.dist_jitter) {
836af83b5   Dave Taht   netem: support de...
1129
1130
1131
1132
1133
1134
1135
1136
  		slot = q->slot_config;
  		if (slot.max_packets == INT_MAX)
  			slot.max_packets = 0;
  		if (slot.max_bytes == INT_MAX)
  			slot.max_bytes = 0;
  		if (nla_put(skb, TCA_NETEM_SLOT, sizeof(slot), &slot))
  			goto nla_put_failure;
  	}
861d7f745   stephen hemminger   netem: cleanup du...
1137
  	return nla_nest_end(skb, nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138

1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
1139
  nla_put_failure:
861d7f745   stephen hemminger   netem: cleanup du...
1140
  	nlmsg_trim(skb, nla);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1141
1142
  	return -1;
  }
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1143
1144
1145
1146
  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...
1147
  	if (cl != 1 || !q->qdisc) 	/* only one class */
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1148
1149
1150
1151
1152
1153
1154
1155
1156
  		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,
653d6fd68   Alexander Aring   net: sched: sch: ...
1157
  		     struct Qdisc **old, struct netlink_ext_ack *extack)
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1158
1159
  {
  	struct netem_sched_data *q = qdisc_priv(sch);
86a7996cc   WANG Cong   net_sched: introd...
1160
  	*old = qdisc_replace(sch, new, &q->qdisc);
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1161
1162
1163
1164
1165
1166
1167
1168
  	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...
1169
  static unsigned long netem_find(struct Qdisc *sch, u32 classid)
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1170
1171
1172
  {
  	return 1;
  }
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
  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...
1188
  	.find		=	netem_find,
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1189
1190
1191
  	.walk		=	netem_walk,
  	.dump		=	netem_dump_class,
  };
20fea08b5   Eric Dumazet   [NET]: Move Qdisc...
1192
  static struct Qdisc_ops netem_qdisc_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1193
  	.id		=	"netem",
10f6dfcfd   stephen hemminger   Revert "sch_netem...
1194
  	.cl_ops		=	&netem_class_ops,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1195
1196
1197
  	.priv_size	=	sizeof(struct netem_sched_data),
  	.enqueue	=	netem_enqueue,
  	.dequeue	=	netem_dequeue,
77be155cb   Jarek Poplawski   pkt_sched: Add pe...
1198
  	.peek		=	qdisc_peek_dequeued,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
  	.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...
1210
1211
  	pr_info("netem: version " VERSION "
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1212
1213
1214
1215
1216
1217
1218
1219
1220
  	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");