Blame view
net/sched/sch_netem.c
31.3 KB
84a14ae8c treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 |
/* * net/sched/sch_netem.c Network emulator * |
1da177e4c Linux-2.6.12-rc2 |
5 |
* Many of the algorithms and ideas for this came from |
10297b993 [NET] SCHED: Fix ... |
6 |
* NIST Net which is not copyrighted. |
1da177e4c 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 net: remove mm.h ... |
11 |
#include <linux/mm.h> |
1da177e4c Linux-2.6.12-rc2 |
12 |
#include <linux/module.h> |
5a0e3ad6a include cleanup: ... |
13 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
14 15 16 |
#include <linux/types.h> #include <linux/kernel.h> #include <linux/errno.h> |
1da177e4c Linux-2.6.12-rc2 |
17 |
#include <linux/skbuff.h> |
78776d3f2 sch_netem: Need t... |
18 |
#include <linux/vmalloc.h> |
1da177e4c Linux-2.6.12-rc2 |
19 |
#include <linux/rtnetlink.h> |
90b41a1cd netem: add cell c... |
20 |
#include <linux/reciprocal_div.h> |
aec0a40a6 netem: use rb tre... |
21 |
#include <linux/rbtree.h> |
1da177e4c Linux-2.6.12-rc2 |
22 |
|
dc5fc579b [NETLINK]: Use nl... |
23 |
#include <net/netlink.h> |
1da177e4c Linux-2.6.12-rc2 |
24 |
#include <net/pkt_sched.h> |
e4ae004b8 netem: add ECN ca... |
25 |
#include <net/inet_ecn.h> |
1da177e4c Linux-2.6.12-rc2 |
26 |
|
250a65f78 netem: update ver... |
27 |
#define VERSION "1.3" |
eb229c4cd [NETEM]: Add vers... |
28 |
|
1da177e4c 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 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 Linux-2.6.12-rc2 |
64 |
*/ |
0a9fe5c37 netem: slotting w... |
65 66 |
struct disttable { u32 size; |
b90feaff2 net: sched: Repla... |
67 |
s16 table[]; |
0a9fe5c37 netem: slotting w... |
68 |
}; |
1da177e4c Linux-2.6.12-rc2 |
69 |
struct netem_sched_data { |
aec0a40a6 netem: use rb tre... |
70 71 |
/* internal t(ime)fifo qdisc uses t_root and sch->limit */ struct rb_root t_root; |
50612537e netem: fix classf... |
72 |
|
d66280b12 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 netem: fix classf... |
76 |
/* optional qdisc for classful handling (NULL at netem init) */ |
1da177e4c Linux-2.6.12-rc2 |
77 |
struct Qdisc *qdisc; |
50612537e netem: fix classf... |
78 |
|
59cb5c673 [NET_SCHED]: sch_... |
79 |
struct qdisc_watchdog watchdog; |
1da177e4c Linux-2.6.12-rc2 |
80 |
|
112f9cb65 netem: convert to... |
81 82 |
s64 latency; s64 jitter; |
b407621c3 [NETEM]: use bett... |
83 |
|
1da177e4c Linux-2.6.12-rc2 |
84 |
u32 loss; |
e4ae004b8 netem: add ECN ca... |
85 |
u32 ecn; |
1da177e4c Linux-2.6.12-rc2 |
86 87 88 |
u32 limit; u32 counter; u32 gap; |
1da177e4c Linux-2.6.12-rc2 |
89 |
u32 duplicate; |
0dca51d36 [PKT_SCHED] netem... |
90 |
u32 reorder; |
c865e5d99 [PKT_SCHED] netem... |
91 |
u32 corrupt; |
6a031f67c sch_netem: suppor... |
92 |
u64 rate; |
90b41a1cd netem: add cell c... |
93 94 |
s32 packet_overhead; u32 cell_size; |
809fa972f reciprocal_divide... |
95 |
struct reciprocal_value cell_size_reciprocal; |
90b41a1cd netem: add cell c... |
96 |
s32 cell_overhead; |
1da177e4c Linux-2.6.12-rc2 |
97 98 |
struct crndstate { |
b407621c3 [NETEM]: use bett... |
99 100 |
u32 last; u32 rho; |
c865e5d99 [PKT_SCHED] netem... |
101 |
} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor; |
1da177e4c Linux-2.6.12-rc2 |
102 |
|
0a9fe5c37 netem: slotting w... |
103 |
struct disttable *delay_dist; |
661b79725 netem: revised co... |
104 105 106 107 108 109 |
enum { CLG_RANDOM, CLG_4_STATES, CLG_GILB_ELL, } loss_model; |
a6e2fe17e 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 sch_netem: replac... |
116 117 118 119 |
enum { GOOD_STATE = 1, BAD_STATE, } GE_state_model; |
661b79725 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 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 netem: slotting w... |
138 |
struct disttable *slot_dist; |
1da177e4c Linux-2.6.12-rc2 |
139 |
}; |
50612537e 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 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 netem: fix classf... |
146 |
*/ |
1da177e4c Linux-2.6.12-rc2 |
147 |
struct netem_skb_cb { |
112f9cb65 netem: convert to... |
148 |
u64 time_to_send; |
1da177e4c Linux-2.6.12-rc2 |
149 |
}; |
5f86173bd net_sched: Add qd... |
150 151 |
static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { |
aec0a40a6 netem: use rb tre... |
152 |
/* we assume we can use skb next/prev/tstamp as storage for rb_node */ |
16bda13d9 net: Make qdisc_s... |
153 |
qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb)); |
175f9c1bb net_sched: Add si... |
154 |
return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; |
5f86173bd net_sched: Add qd... |
155 |
} |
1da177e4c 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 net: replace macr... |
162 |
state->last = prandom_u32(); |
1da177e4c 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 [NETEM]: use bett... |
169 |
static u32 get_crandom(struct crndstate *state) |
1da177e4c Linux-2.6.12-rc2 |
170 171 172 |
{ u64 value, rho; unsigned long answer; |
0a9fe5c37 netem: slotting w... |
173 |
if (!state || state->rho == 0) /* no correlation */ |
63862b5be net: replace macr... |
174 |
return prandom_u32(); |
1da177e4c Linux-2.6.12-rc2 |
175 |
|
63862b5be net: replace macr... |
176 |
value = prandom_u32(); |
1da177e4c 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 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 net: replace macr... |
189 |
u32 rnd = prandom_u32(); |
661b79725 netem: revised co... |
190 191 |
/* |
25985edce Fix common misspe... |
192 |
* Makes a comparison between rnd and the transition |
661b79725 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 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 netem: revised co... |
200 201 |
*/ switch (clg->state) { |
a6e2fe17e sch_netem: replac... |
202 |
case TX_IN_GAP_PERIOD: |
661b79725 netem: revised co... |
203 |
if (rnd < clg->a4) { |
a6e2fe17e sch_netem: replac... |
204 |
clg->state = LOST_IN_BURST_PERIOD; |
661b79725 netem: revised co... |
205 |
return true; |
ab6c27be8 netem: fix loss 4... |
206 |
} else if (clg->a4 < rnd && rnd < clg->a1 + clg->a4) { |
a6e2fe17e sch_netem: replac... |
207 |
clg->state = LOST_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
208 |
return true; |
a6e2fe17e sch_netem: replac... |
209 210 211 |
} else if (clg->a1 + clg->a4 < rnd) { clg->state = TX_IN_GAP_PERIOD; } |
661b79725 netem: revised co... |
212 213 |
break; |
a6e2fe17e sch_netem: replac... |
214 |
case TX_IN_BURST_PERIOD: |
661b79725 netem: revised co... |
215 |
if (rnd < clg->a5) { |
a6e2fe17e sch_netem: replac... |
216 |
clg->state = LOST_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
217 |
return true; |
a6e2fe17e sch_netem: replac... |
218 219 220 |
} else { clg->state = TX_IN_BURST_PERIOD; } |
661b79725 netem: revised co... |
221 222 |
break; |
a6e2fe17e sch_netem: replac... |
223 |
case LOST_IN_GAP_PERIOD: |
661b79725 netem: revised co... |
224 |
if (rnd < clg->a3) |
a6e2fe17e sch_netem: replac... |
225 |
clg->state = TX_IN_BURST_PERIOD; |
661b79725 netem: revised co... |
226 |
else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) { |
a6e2fe17e sch_netem: replac... |
227 |
clg->state = TX_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
228 |
} else if (clg->a2 + clg->a3 < rnd) { |
a6e2fe17e sch_netem: replac... |
229 |
clg->state = LOST_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
230 231 232 |
return true; } break; |
a6e2fe17e sch_netem: replac... |
233 234 |
case LOST_IN_BURST_PERIOD: clg->state = TX_IN_GAP_PERIOD; |
661b79725 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 Fix common misspe... |
245 |
* Makes a comparison between random number and the transition |
661b79725 netem: revised co... |
246 |
* probabilities outgoing from the current state, then decides the |
25985edce Fix common misspe... |
247 |
* next state. A second random number is extracted and the comparison |
661b79725 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 sch_netem: replac... |
256 |
case GOOD_STATE: |
63862b5be net: replace macr... |
257 |
if (prandom_u32() < clg->a1) |
c045a734d sch_netem: replac... |
258 |
clg->state = BAD_STATE; |
63862b5be net: replace macr... |
259 |
if (prandom_u32() < clg->a4) |
661b79725 netem: revised co... |
260 |
return true; |
7c2781fa9 netem: missing br... |
261 |
break; |
c045a734d sch_netem: replac... |
262 |
case BAD_STATE: |
63862b5be net: replace macr... |
263 |
if (prandom_u32() < clg->a2) |
c045a734d sch_netem: replac... |
264 |
clg->state = GOOD_STATE; |
63862b5be net: replace macr... |
265 |
if (prandom_u32() > clg->a3) |
661b79725 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 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 netem: remove unn... |
302 |
static s64 tabledist(s64 mu, s32 sigma, |
112f9cb65 netem: convert to... |
303 |
struct crndstate *state, |
9b0ed8917 netem: remove unn... |
304 |
const struct disttable *dist) |
1da177e4c Linux-2.6.12-rc2 |
305 |
{ |
112f9cb65 netem: convert to... |
306 |
s64 x; |
b407621c3 [NETEM]: use bett... |
307 308 |
long t; u32 rnd; |
1da177e4c 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 [NET] SCHED: Fix ... |
316 |
if (dist == NULL) |
eadd1befd netem: fix zero d... |
317 |
return ((rnd % (2 * (u32)sigma)) + mu) - sigma; |
1da177e4c 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 netem: use 64 bit... |
328 |
static u64 packet_time_ns(u64 len, const struct netem_sched_data *q) |
7bc0f28c7 netem: rate exten... |
329 |
{ |
90b41a1cd 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 netem: use 64 bit... |
339 340 |
return div64_u64(len * NSEC_PER_SEC, q->rate); |
7bc0f28c7 netem: rate exten... |
341 |
} |
ff704050f netem: free skb's... |
342 343 344 |
static void tfifo_reset(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); |
3aa605f28 sch_netem: faster... |
345 |
struct rb_node *p = rb_first(&q->t_root); |
ff704050f netem: free skb's... |
346 |
|
3aa605f28 sch_netem: faster... |
347 |
while (p) { |
18a4c0eab net: add rb_to_sk... |
348 |
struct sk_buff *skb = rb_to_skb(p); |
ff704050f netem: free skb's... |
349 |
|
3aa605f28 sch_netem: faster... |
350 351 |
p = rb_next(p); rb_erase(&skb->rbnode, &q->t_root); |
2f08a9a16 net_sched: sch_ne... |
352 |
rtnl_kfree_skbs(skb, skb); |
ff704050f netem: free skb's... |
353 |
} |
d66280b12 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 netem: free skb's... |
358 |
} |
960fb66e5 netem: add limita... |
359 |
static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) |
50612537e netem: fix classf... |
360 |
{ |
aec0a40a6 netem: use rb tre... |
361 |
struct netem_sched_data *q = qdisc_priv(sch); |
112f9cb65 netem: convert to... |
362 |
u64 tnext = netem_skb_cb(nskb)->time_to_send; |
50612537e netem: fix classf... |
363 |
|
d66280b12 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 netem: use rb tre... |
367 |
else |
d66280b12 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 netem: fix classf... |
385 |
} |
aec0a40a6 netem: use rb tre... |
386 |
sch->q.qlen++; |
50612537e netem: fix classf... |
387 |
} |
6071bd1aa 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 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 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 net_sched: drop p... |
401 |
qdisc_drop(skb, sch, to_free); |
6071bd1aa netem: Segment GS... |
402 403 404 405 406 |
return NULL; } consume_skb(skb); return segs; } |
0afb51e72 [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 net_sched: drop p... |
413 414 |
static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) |
1da177e4c Linux-2.6.12-rc2 |
415 416 |
{ struct netem_sched_data *q = qdisc_priv(sch); |
89e1df74f [PKT_SCHED] netem... |
417 418 |
/* We don't fill cb now as skb_unshare() may invalidate it */ struct netem_skb_cb *cb; |
0afb51e72 [PKT_SCHED]: nete... |
419 |
struct sk_buff *skb2; |
6071bd1aa netem: Segment GS... |
420 |
struct sk_buff *segs = NULL; |
177b80074 net: netem: fix b... |
421 |
unsigned int prev_len = qdisc_pkt_len(skb); |
0afb51e72 [PKT_SCHED]: nete... |
422 |
int count = 1; |
6071bd1aa netem: Segment GS... |
423 |
int rc = NET_XMIT_SUCCESS; |
5845f7063 net: netem: fix s... |
424 |
int rc_drop = NET_XMIT_DROP; |
1da177e4c Linux-2.6.12-rc2 |
425 |
|
9410d386d net: Prevent inva... |
426 427 |
/* Do not fool qdisc_drop_all() */ skb->prev = NULL; |
0afb51e72 [PKT_SCHED]: nete... |
428 429 430 |
/* Random duplication */ if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)) ++count; |
661b79725 netem: revised co... |
431 |
/* Drop packet? */ |
e4ae004b8 netem: add ECN ca... |
432 433 |
if (loss_event(q)) { if (q->ecn && INET_ECN_set_ce(skb)) |
25331d6ce net: sched: imple... |
434 |
qdisc_qstats_drop(sch); /* mark packet */ |
e4ae004b8 netem: add ECN ca... |
435 436 437 |
else --count; } |
0afb51e72 [PKT_SCHED]: nete... |
438 |
if (count == 0) { |
25331d6ce net: sched: imple... |
439 |
qdisc_qstats_drop(sch); |
520ac30f4 net_sched: drop p... |
440 |
__qdisc_drop(skb, to_free); |
c27f339af net_sched: Add qd... |
441 |
return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; |
1da177e4c Linux-2.6.12-rc2 |
442 |
} |
5a308f40b 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 netem: refine ear... |
445 |
*/ |
5080f39e8 netem: apply corr... |
446 |
if (q->latency || q->jitter || q->rate) |
f2f872f92 netem: Introduce ... |
447 |
skb_orphan_partial(skb); |
4e8a52015 [PKT_SCHED] netem... |
448 |
|
0afb51e72 [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 sch_netem: fix rc... |
455 |
struct Qdisc *rootq = qdisc_root_bh(sch); |
0afb51e72 [PKT_SCHED]: nete... |
456 |
u32 dupsave = q->duplicate; /* prevent duplicating a dup... */ |
0afb51e72 [PKT_SCHED]: nete... |
457 |
|
b396cca6f net: sched: depre... |
458 |
q->duplicate = 0; |
520ac30f4 net_sched: drop p... |
459 |
rootq->enqueue(skb2, rootq, to_free); |
0afb51e72 [PKT_SCHED]: nete... |
460 |
q->duplicate = dupsave; |
5845f7063 net: netem: fix s... |
461 |
rc_drop = NET_XMIT_SUCCESS; |
1da177e4c Linux-2.6.12-rc2 |
462 |
} |
c865e5d99 [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 netem: Segment GS... |
470 |
if (skb_is_gso(skb)) { |
3e14c383d net: netem: fix u... |
471 472 |
skb = netem_segment(skb, sch, to_free); if (!skb) |
5845f7063 net: netem: fix s... |
473 |
return rc_drop; |
3e14c383d 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 netem: Segment GS... |
477 |
} |
8a6e9c670 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 net: netem: fix e... |
486 |
skb = NULL; |
6071bd1aa netem: Segment GS... |
487 488 |
goto finish_segs; } |
c865e5d99 [PKT_SCHED] netem... |
489 |
|
63862b5be net: replace macr... |
490 491 |
skb->data[prandom_u32() % skb_headlen(skb)] ^= 1<<(prandom_u32() % 8); |
c865e5d99 [PKT_SCHED] netem... |
492 |
} |
5845f7063 net: netem: fix s... |
493 |
if (unlikely(sch->q.qlen >= sch->limit)) { |
3e14c383d net: netem: fix u... |
494 495 |
/* re-link segs, so that qdisc_drop_all() frees them all */ skb->next = segs; |
5845f7063 net: netem: fix s... |
496 497 498 |
qdisc_drop_all(skb, sch, to_free); return rc_drop; } |
960fb66e5 netem: add limita... |
499 |
|
25331d6ce net: sched: imple... |
500 |
qdisc_qstats_backlog_inc(sch, skb); |
960fb66e5 netem: add limita... |
501 |
|
5f86173bd net_sched: Add qd... |
502 |
cb = netem_skb_cb(skb); |
cc7ec456f net_sched: cleanups |
503 |
if (q->gap == 0 || /* not doing reordering */ |
a42b4799c netem: Fix off-by... |
504 |
q->counter < q->gap - 1 || /* inside last reordering gap */ |
f64f9e719 net: Move && and ... |
505 |
q->reorder < get_crandom(&q->reorder_cor)) { |
112f9cb65 netem: convert to... |
506 507 |
u64 now; s64 delay; |
07aaa1154 [NETEM]: use PSCH... |
508 509 510 |
delay = tabledist(q->latency, q->jitter, &q->delay_cor, q->delay_dist); |
112f9cb65 netem: convert to... |
511 |
now = ktime_get_ns(); |
7bc0f28c7 netem: rate exten... |
512 513 |
if (q->rate) { |
5080f39e8 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 net: add rb_to_sk... |
521 |
t_skb = skb_rb_last(&q->t_root); |
5080f39e8 netem: apply corr... |
522 523 |
t_last = netem_skb_cb(t_skb); if (!last || |
d66280b12 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 netem: apply corr... |
533 |
last = t_last; |
5080f39e8 netem: apply corr... |
534 |
} |
7bc0f28c7 netem: rate exten... |
535 |
|
aec0a40a6 netem: use rb tre... |
536 |
if (last) { |
7bc0f28c7 netem: rate exten... |
537 |
/* |
a13d31047 netem: fix delay ... |
538 539 |
* Last packet in queue is reference point (now), * calculate this time bonus and subtract |
7bc0f28c7 netem: rate exten... |
540 541 |
* from delay. */ |
5080f39e8 netem: apply corr... |
542 |
delay -= last->time_to_send - now; |
112f9cb65 netem: convert to... |
543 |
delay = max_t(s64, 0, delay); |
5080f39e8 netem: apply corr... |
544 |
now = last->time_to_send; |
7bc0f28c7 netem: rate exten... |
545 |
} |
a13d31047 netem: fix delay ... |
546 |
|
bce552fd6 netem: use 64 bit... |
547 |
delay += packet_time_ns(qdisc_pkt_len(skb), q); |
7bc0f28c7 netem: rate exten... |
548 |
} |
7c59e25f3 [NET_SCHED]: kill... |
549 |
cb->time_to_send = now + delay; |
1da177e4c Linux-2.6.12-rc2 |
550 |
++q->counter; |
960fb66e5 netem: add limita... |
551 |
tfifo_enqueue(skb, sch); |
1da177e4c Linux-2.6.12-rc2 |
552 |
} else { |
10297b993 [NET] SCHED: Fix ... |
553 |
/* |
0dca51d36 [PKT_SCHED] netem... |
554 555 556 |
* Do re-ordering by putting one out of N packets at the front * of the queue. */ |
112f9cb65 netem: convert to... |
557 |
cb->time_to_send = ktime_get_ns(); |
0dca51d36 [PKT_SCHED] netem... |
558 |
q->counter = 0; |
8ba25dad0 sch_netem: Replac... |
559 |
|
596977300 sch_netem: Move p... |
560 |
__qdisc_enqueue_head(skb, &sch->q); |
eb1019244 net_sched: Bug in... |
561 |
sch->qstats.requeues++; |
378a2f090 net_sched: Add qd... |
562 |
} |
1da177e4c Linux-2.6.12-rc2 |
563 |
|
6071bd1aa netem: Segment GS... |
564 565 |
finish_segs: if (segs) { |
177b80074 net: netem: fix b... |
566 |
unsigned int len, last_len; |
a7fa12d15 net: netem: fix e... |
567 |
int nb; |
177b80074 net: netem: fix b... |
568 |
|
a7fa12d15 net: netem: fix e... |
569 570 |
len = skb ? skb->len : 0; nb = skb ? 1 : 0; |
177b80074 net: netem: fix b... |
571 |
|
6071bd1aa netem: Segment GS... |
572 573 |
while (segs) { skb2 = segs->next; |
a8305bff6 net: Add and use ... |
574 |
skb_mark_not_on_list(segs); |
6071bd1aa netem: Segment GS... |
575 576 |
qdisc_skb_cb(segs)->pkt_len = segs->len; last_len = segs->len; |
520ac30f4 net_sched: drop p... |
577 |
rc = qdisc_enqueue(segs, sch, to_free); |
6071bd1aa 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 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 net: netem: corre... |
589 590 |
} else if (!skb) { return NET_XMIT_DROP; |
6071bd1aa netem: Segment GS... |
591 |
} |
10f6dfcfd Revert "sch_netem... |
592 |
return NET_XMIT_SUCCESS; |
1da177e4c Linux-2.6.12-rc2 |
593 |
} |
836af83b5 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 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 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 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 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 netem: fix classf... |
647 |
tfifo_dequeue: |
ed760cb8a sched: replace __... |
648 |
skb = __qdisc_dequeue_head(&sch->q); |
771018e76 [PKT_SCHED]: nete... |
649 |
if (skb) { |
25331d6ce net: sched: imple... |
650 |
qdisc_qstats_backlog_dec(sch, skb); |
0ad2a8365 netem: Fixes byte... |
651 |
deliver: |
aec0a40a6 netem: use rb tre... |
652 653 654 |
qdisc_bstats_update(sch, skb); return skb; } |
d66280b12 net: netem: use a... |
655 656 |
skb = netem_peek(q); if (skb) { |
112f9cb65 netem: convert to... |
657 |
u64 time_to_send; |
836af83b5 netem: support de... |
658 |
u64 now = ktime_get_ns(); |
36b7bfe09 netem: fix possib... |
659 |
|
0f9f32ac6 [PKT_SCHED] netem... |
660 |
/* if more time remaining? */ |
36b7bfe09 netem: fix possib... |
661 |
time_to_send = netem_skb_cb(skb)->time_to_send; |
836af83b5 netem: support de... |
662 663 |
if (q->slot.slot_next && q->slot.slot_next < time_to_send) get_slot_next(q, now); |
aec0a40a6 netem: use rb tre... |
664 |
|
d66280b12 net: netem: use a... |
665 666 |
if (time_to_send <= now && q->slot.slot_next <= now) { netem_erase_head(q, skb); |
aec0a40a6 netem: use rb tre... |
667 |
sch->q.qlen--; |
0ad2a8365 netem: Fixes byte... |
668 |
qdisc_qstats_backlog_dec(sch, skb); |
aec0a40a6 netem: use rb tre... |
669 670 |
skb->next = NULL; skb->prev = NULL; |
bffa72cf7 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 pkt_sched: Use qd... |
675 |
|
836af83b5 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 netem: fix classf... |
683 |
if (q->qdisc) { |
21de12ee5 netem: fix a use ... |
684 |
unsigned int pkt_len = qdisc_pkt_len(skb); |
520ac30f4 net_sched: drop p... |
685 686 |
struct sk_buff *to_free = NULL; int err; |
50612537e netem: fix classf... |
687 |
|
520ac30f4 net_sched: drop p... |
688 689 |
err = qdisc_enqueue(skb, q->qdisc, &to_free); kfree_skb_list(to_free); |
21de12ee5 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 netem: fix classf... |
695 696 697 |
} goto tfifo_dequeue; } |
aec0a40a6 netem: use rb tre... |
698 |
goto deliver; |
07aaa1154 [NETEM]: use PSCH... |
699 |
} |
11274e5a4 [NETEM]: avoid ex... |
700 |
|
50612537e netem: fix classf... |
701 702 703 704 705 |
if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) goto deliver; } |
836af83b5 netem: support de... |
706 707 708 709 |
qdisc_watchdog_schedule_ns(&q->watchdog, max(time_to_send, q->slot.slot_next)); |
0f9f32ac6 [PKT_SCHED] netem... |
710 |
} |
50612537e netem: fix classf... |
711 712 713 714 715 |
if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) goto deliver; } |
0f9f32ac6 [PKT_SCHED] netem... |
716 |
return NULL; |
1da177e4c Linux-2.6.12-rc2 |
717 |
} |
1da177e4c Linux-2.6.12-rc2 |
718 719 720 |
static void netem_reset(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); |
50612537e netem: fix classf... |
721 |
qdisc_reset_queue(sch); |
ff704050f netem: free skb's... |
722 |
tfifo_reset(sch); |
50612537e netem: fix classf... |
723 724 |
if (q->qdisc) qdisc_reset(q->qdisc); |
59cb5c673 [NET_SCHED]: sch_... |
725 |
qdisc_watchdog_cancel(&q->watchdog); |
1da177e4c Linux-2.6.12-rc2 |
726 |
} |
6373a9a28 netem: use vmallo... |
727 728 |
static void dist_free(struct disttable *d) { |
4cb28970a net: use the new ... |
729 |
kvfree(d); |
6373a9a28 netem: use vmallo... |
730 |
} |
1da177e4c Linux-2.6.12-rc2 |
731 732 733 734 |
/* * Distribution data is a variable size payload containing * signed 16 bit values. */ |
836af83b5 netem: support de... |
735 |
|
0a9fe5c37 netem: slotting w... |
736 737 |
static int get_dist_table(struct Qdisc *sch, struct disttable **tbl, const struct nlattr *attr) |
1da177e4c Linux-2.6.12-rc2 |
738 |
{ |
6373a9a28 netem: use vmallo... |
739 |
size_t n = nla_len(attr)/sizeof(__s16); |
1e90474c3 [NET_SCHED]: Conv... |
740 |
const __s16 *data = nla_data(attr); |
7698b4fca pkt_sched: Add an... |
741 |
spinlock_t *root_lock; |
1da177e4c Linux-2.6.12-rc2 |
742 743 |
struct disttable *d; int i; |
b41d936b5 sch_netem: fix a ... |
744 |
if (!n || n > NETEM_DIST_MAX) |
1da177e4c Linux-2.6.12-rc2 |
745 |
return -EINVAL; |
752ade68c treewide: use kv[... |
746 |
d = kvmalloc(sizeof(struct disttable) + n * sizeof(s16), GFP_KERNEL); |
1da177e4c 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 [NET] SCHED: Fix ... |
753 |
|
102396ae6 pkt_sched: Fix lo... |
754 |
root_lock = qdisc_root_sleeping_lock(sch); |
7698b4fca pkt_sched: Add an... |
755 756 |
spin_lock_bh(root_lock); |
0a9fe5c37 netem: slotting w... |
757 |
swap(*tbl, d); |
7698b4fca pkt_sched: Add an... |
758 |
spin_unlock_bh(root_lock); |
bb52c7acf netem: dont call ... |
759 760 |
dist_free(d); |
1da177e4c Linux-2.6.12-rc2 |
761 762 |
return 0; } |
836af83b5 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 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 netem: support de... |
775 776 |
q->slot.packets_left = q->slot_config.max_packets; q->slot.bytes_left = q->slot_config.max_bytes; |
0a9fe5c37 netem: slotting w... |
777 778 |
if (q->slot_config.min_delay | q->slot_config.max_delay | q->slot_config.dist_jitter) |
836af83b5 netem: support de... |
779 780 781 782 |
q->slot.slot_next = ktime_get_ns(); else q->slot.slot_next = 0; } |
49545a777 sch_netem: change... |
783 |
static void get_correlation(struct netem_sched_data *q, const struct nlattr *attr) |
1da177e4c Linux-2.6.12-rc2 |
784 |
{ |
1e90474c3 [NET_SCHED]: Conv... |
785 |
const struct tc_netem_corr *c = nla_data(attr); |
1da177e4c Linux-2.6.12-rc2 |
786 |
|
1da177e4c 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 Linux-2.6.12-rc2 |
790 |
} |
49545a777 sch_netem: change... |
791 |
static void get_reorder(struct netem_sched_data *q, const struct nlattr *attr) |
0dca51d36 [PKT_SCHED] netem... |
792 |
{ |
1e90474c3 [NET_SCHED]: Conv... |
793 |
const struct tc_netem_reorder *r = nla_data(attr); |
0dca51d36 [PKT_SCHED] netem... |
794 |
|
0dca51d36 [PKT_SCHED] netem... |
795 796 |
q->reorder = r->probability; init_crandom(&q->reorder_cor, r->correlation); |
0dca51d36 [PKT_SCHED] netem... |
797 |
} |
49545a777 sch_netem: change... |
798 |
static void get_corrupt(struct netem_sched_data *q, const struct nlattr *attr) |
c865e5d99 [PKT_SCHED] netem... |
799 |
{ |
1e90474c3 [NET_SCHED]: Conv... |
800 |
const struct tc_netem_corrupt *r = nla_data(attr); |
c865e5d99 [PKT_SCHED] netem... |
801 |
|
c865e5d99 [PKT_SCHED] netem... |
802 803 |
q->corrupt = r->probability; init_crandom(&q->corrupt_cor, r->correlation); |
c865e5d99 [PKT_SCHED] netem... |
804 |
} |
49545a777 sch_netem: change... |
805 |
static void get_rate(struct netem_sched_data *q, const struct nlattr *attr) |
7bc0f28c7 netem: rate exten... |
806 |
{ |
7bc0f28c7 netem: rate exten... |
807 808 809 |
const struct tc_netem_rate *r = nla_data(attr); q->rate = r->rate; |
90b41a1cd netem: add cell c... |
810 811 |
q->packet_overhead = r->packet_overhead; q->cell_size = r->cell_size; |
809fa972f reciprocal_divide... |
812 |
q->cell_overhead = r->cell_overhead; |
90b41a1cd netem: add cell c... |
813 814 |
if (q->cell_size) q->cell_size_reciprocal = reciprocal_value(q->cell_size); |
809fa972f reciprocal_divide... |
815 816 |
else q->cell_size_reciprocal = (struct reciprocal_value) { 0 }; |
7bc0f28c7 netem: rate exten... |
817 |
} |
49545a777 sch_netem: change... |
818 |
static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr) |
661b79725 netem: revised co... |
819 |
{ |
661b79725 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 net_sched: add sp... |
825 |
switch (type) { |
661b79725 netem: revised co... |
826 827 |
case NETEM_LOSS_GI: { const struct tc_netem_gimodel *gi = nla_data(la); |
2494654d4 netem: loss model... |
828 |
if (nla_len(la) < sizeof(struct tc_netem_gimodel)) { |
661b79725 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 sch_netem: replac... |
835 |
q->clg.state = TX_IN_GAP_PERIOD; |
661b79725 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 netem: loss model... |
846 847 848 |
if (nla_len(la) < sizeof(struct tc_netem_gemodel)) { pr_info("netem: incorrect ge model size "); |
661b79725 netem: revised co... |
849 850 851 852 |
return -EINVAL; } q->loss_model = CLG_GILB_ELL; |
3fbac2a87 sch_netem: replac... |
853 |
q->clg.state = GOOD_STATE; |
661b79725 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 [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 netem: rate exten... |
874 |
[TCA_NETEM_RATE] = { .len = sizeof(struct tc_netem_rate) }, |
661b79725 netem: revised co... |
875 |
[TCA_NETEM_LOSS] = { .type = NLA_NESTED }, |
e4ae004b8 netem: add ECN ca... |
876 |
[TCA_NETEM_ECN] = { .type = NLA_U32 }, |
6a031f67c sch_netem: suppor... |
877 |
[TCA_NETEM_RATE64] = { .type = NLA_U64 }, |
99803171e netem: add uapi t... |
878 879 |
[TCA_NETEM_LATENCY64] = { .type = NLA_S64 }, [TCA_NETEM_JITTER64] = { .type = NLA_S64 }, |
836af83b5 netem: support de... |
880 |
[TCA_NETEM_SLOT] = { .len = sizeof(struct tc_netem_slot) }, |
27a3421e4 [NET_SCHED]: Use ... |
881 |
}; |
2c10b32bf 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 netem: revised co... |
886 887 888 |
if (nested_len < 0) { pr_info("netem: invalid attributes len %d ", nested_len); |
2c10b32bf netlink: Remove c... |
889 |
return -EINVAL; |
661b79725 netem: revised co... |
890 |
} |
2c10b32bf netlink: Remove c... |
891 |
if (nested_len >= nla_attr_size(0)) |
8cb081746 netlink: make val... |
892 893 894 |
return nla_parse_deprecated(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), nested_len, policy, NULL); |
661b79725 netem: revised co... |
895 |
|
2c10b32bf netlink: Remove c... |
896 897 898 |
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); return 0; } |
c865e5d99 [PKT_SCHED] netem... |
899 |
/* Parse netlink message to set options */ |
2030721cc net: sched: sch: ... |
900 901 |
static int netem_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
902 903 |
{ struct netem_sched_data *q = qdisc_priv(sch); |
b03f46720 [NET_SCHED]: sch_... |
904 |
struct nlattr *tb[TCA_NETEM_MAX + 1]; |
1da177e4c Linux-2.6.12-rc2 |
905 |
struct tc_netem_qopt *qopt; |
54a4b05cd sch_netem: return... |
906 907 |
struct clgstate old_clg; int old_loss_model = CLG_RANDOM; |
1da177e4c Linux-2.6.12-rc2 |
908 |
int ret; |
10297b993 [NET] SCHED: Fix ... |
909 |
|
b03f46720 [NET_SCHED]: sch_... |
910 |
if (opt == NULL) |
1da177e4c Linux-2.6.12-rc2 |
911 |
return -EINVAL; |
2c10b32bf netlink: Remove c... |
912 913 |
qopt = nla_data(opt); ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt)); |
b03f46720 [NET_SCHED]: sch_... |
914 915 |
if (ret < 0) return ret; |
54a4b05cd 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 sch_netem: change... |
921 |
ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]); |
54a4b05cd 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 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 sch_netem: return... |
942 |
} |
50612537e netem: fix classf... |
943 |
sch->limit = qopt->limit; |
10297b993 [NET] SCHED: Fix ... |
944 |
|
112f9cb65 netem: convert to... |
945 946 |
q->latency = PSCHED_TICKS2NS(qopt->latency); q->jitter = PSCHED_TICKS2NS(qopt->jitter); |
1da177e4c Linux-2.6.12-rc2 |
947 948 |
q->limit = qopt->limit; q->gap = qopt->gap; |
0dca51d36 [PKT_SCHED] netem... |
949 |
q->counter = 0; |
1da177e4c Linux-2.6.12-rc2 |
950 951 |
q->loss = qopt->loss; q->duplicate = qopt->duplicate; |
bb2f8cc0e [NETEM]: spelling... |
952 953 |
/* for compatibility with earlier versions. * if gap is set, need to assume 100% probability |
0dca51d36 [PKT_SCHED] netem... |
954 |
*/ |
a362e0a78 [NETEM]: report r... |
955 956 |
if (q->gap) q->reorder = ~0; |
0dca51d36 [PKT_SCHED] netem... |
957 |
|
265eb67fb netem: eliminate ... |
958 |
if (tb[TCA_NETEM_CORR]) |
49545a777 sch_netem: change... |
959 |
get_correlation(q, tb[TCA_NETEM_CORR]); |
1da177e4c Linux-2.6.12-rc2 |
960 |
|
265eb67fb netem: eliminate ... |
961 |
if (tb[TCA_NETEM_REORDER]) |
49545a777 sch_netem: change... |
962 |
get_reorder(q, tb[TCA_NETEM_REORDER]); |
1da177e4c Linux-2.6.12-rc2 |
963 |
|
265eb67fb netem: eliminate ... |
964 |
if (tb[TCA_NETEM_CORRUPT]) |
49545a777 sch_netem: change... |
965 |
get_corrupt(q, tb[TCA_NETEM_CORRUPT]); |
1da177e4c Linux-2.6.12-rc2 |
966 |
|
7bc0f28c7 netem: rate exten... |
967 |
if (tb[TCA_NETEM_RATE]) |
49545a777 sch_netem: change... |
968 |
get_rate(q, tb[TCA_NETEM_RATE]); |
7bc0f28c7 netem: rate exten... |
969 |
|
6a031f67c 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 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 netem: add ECN ca... |
978 979 |
if (tb[TCA_NETEM_ECN]) q->ecn = nla_get_u32(tb[TCA_NETEM_ECN]); |
836af83b5 netem: support de... |
980 981 |
if (tb[TCA_NETEM_SLOT]) get_slot(q, tb[TCA_NETEM_SLOT]); |
eadd1befd 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 netem: revised co... |
984 |
return ret; |
0a9fe5c37 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 Linux-2.6.12-rc2 |
994 |
} |
e63d7dfd2 net: sched: sch: ... |
995 996 |
static int netem_init(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
997 998 999 |
{ struct netem_sched_data *q = qdisc_priv(sch); int ret; |
634576a18 sch_netem: avoid ... |
1000 |
qdisc_watchdog_init(&q->watchdog, sch); |
1da177e4c Linux-2.6.12-rc2 |
1001 1002 |
if (!opt) return -EINVAL; |
661b79725 netem: revised co... |
1003 |
q->loss_model = CLG_RANDOM; |
2030721cc net: sched: sch: ... |
1004 |
ret = netem_change(sch, opt, extack); |
50612537e netem: fix classf... |
1005 |
if (ret) |
250a65f78 netem: update ver... |
1006 1007 |
pr_info("netem: change failed "); |
1da177e4c 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 [NET_SCHED]: sch_... |
1014 |
qdisc_watchdog_cancel(&q->watchdog); |
50612537e netem: fix classf... |
1015 |
if (q->qdisc) |
86bd446b5 net: sched: renam... |
1016 |
qdisc_put(q->qdisc); |
6373a9a28 netem: use vmallo... |
1017 |
dist_free(q->delay_dist); |
0a9fe5c37 netem: slotting w... |
1018 |
dist_free(q->slot_dist); |
1da177e4c Linux-2.6.12-rc2 |
1019 |
} |
661b79725 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 netlink: make nla... |
1024 |
nest = nla_nest_start_noflag(skb, TCA_NETEM_LOSS); |
661b79725 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 pkt_sched: Stop u... |
1042 1043 |
if (nla_put(skb, NETEM_LOSS_GI, sizeof(gi), &gi)) goto nla_put_failure; |
661b79725 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 pkt_sched: Stop u... |
1053 1054 |
if (nla_put(skb, NETEM_LOSS_GE, sizeof(ge), &ge)) goto nla_put_failure; |
661b79725 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 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 netem: cleanup du... |
1069 |
struct nlattr *nla = (struct nlattr *) skb_tail_pointer(skb); |
1da177e4c Linux-2.6.12-rc2 |
1070 1071 |
struct tc_netem_qopt qopt; struct tc_netem_corr cor; |
0dca51d36 [PKT_SCHED] netem... |
1072 |
struct tc_netem_reorder reorder; |
c865e5d99 [PKT_SCHED] netem... |
1073 |
struct tc_netem_corrupt corrupt; |
7bc0f28c7 netem: rate exten... |
1074 |
struct tc_netem_rate rate; |
836af83b5 netem: support de... |
1075 |
struct tc_netem_slot slot; |
1da177e4c Linux-2.6.12-rc2 |
1076 |
|
112f9cb65 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 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 pkt_sched: Stop u... |
1085 1086 |
if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
1087 |
|
99803171e 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 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 pkt_sched: Stop u... |
1096 1097 |
if (nla_put(skb, TCA_NETEM_CORR, sizeof(cor), &cor)) goto nla_put_failure; |
0dca51d36 [PKT_SCHED] netem... |
1098 1099 1100 |
reorder.probability = q->reorder; reorder.correlation = q->reorder_cor.rho; |
1b34ec43c pkt_sched: Stop u... |
1101 1102 |
if (nla_put(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder)) goto nla_put_failure; |
0dca51d36 [PKT_SCHED] netem... |
1103 |
|
c865e5d99 [PKT_SCHED] netem... |
1104 1105 |
corrupt.probability = q->corrupt; corrupt.correlation = q->corrupt_cor.rho; |
1b34ec43c pkt_sched: Stop u... |
1106 1107 |
if (nla_put(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt)) goto nla_put_failure; |
c865e5d99 [PKT_SCHED] netem... |
1108 |
|
6a031f67c sch_netem: suppor... |
1109 |
if (q->rate >= (1ULL << 32)) { |
2a51c1e8e sched: use nla_pu... |
1110 1111 |
if (nla_put_u64_64bit(skb, TCA_NETEM_RATE64, q->rate, TCA_NETEM_PAD)) |
6a031f67c sch_netem: suppor... |
1112 1113 1114 1115 1116 |
goto nla_put_failure; rate.rate = ~0U; } else { rate.rate = q->rate; } |
90b41a1cd 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 pkt_sched: Stop u... |
1120 1121 |
if (nla_put(skb, TCA_NETEM_RATE, sizeof(rate), &rate)) goto nla_put_failure; |
7bc0f28c7 netem: rate exten... |
1122 |
|
e4ae004b8 netem: add ECN ca... |
1123 1124 |
if (q->ecn && nla_put_u32(skb, TCA_NETEM_ECN, q->ecn)) goto nla_put_failure; |
661b79725 netem: revised co... |
1125 1126 |
if (dump_loss_model(q, skb) != 0) goto nla_put_failure; |
0a9fe5c37 netem: slotting w... |
1127 1128 |
if (q->slot_config.min_delay | q->slot_config.max_delay | q->slot_config.dist_jitter) { |
836af83b5 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 netem: cleanup du... |
1137 |
return nla_nest_end(skb, nla); |
1da177e4c Linux-2.6.12-rc2 |
1138 |
|
1e90474c3 [NET_SCHED]: Conv... |
1139 |
nla_put_failure: |
861d7f745 netem: cleanup du... |
1140 |
nlmsg_trim(skb, nla); |
1da177e4c Linux-2.6.12-rc2 |
1141 1142 |
return -1; } |
10f6dfcfd 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 netem: fix classf... |
1147 |
if (cl != 1 || !q->qdisc) /* only one class */ |
10f6dfcfd 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 net: sched: sch: ... |
1157 |
struct Qdisc **old, struct netlink_ext_ack *extack) |
10f6dfcfd Revert "sch_netem... |
1158 1159 |
{ struct netem_sched_data *q = qdisc_priv(sch); |
86a7996cc net_sched: introd... |
1160 |
*old = qdisc_replace(sch, new, &q->qdisc); |
10f6dfcfd 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 net_sched: remove... |
1169 |
static unsigned long netem_find(struct Qdisc *sch, u32 classid) |
10f6dfcfd Revert "sch_netem... |
1170 1171 1172 |
{ return 1; } |
10f6dfcfd 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 net_sched: remove... |
1188 |
.find = netem_find, |
10f6dfcfd Revert "sch_netem... |
1189 1190 1191 |
.walk = netem_walk, .dump = netem_dump_class, }; |
20fea08b5 [NET]: Move Qdisc... |
1192 |
static struct Qdisc_ops netem_qdisc_ops __read_mostly = { |
1da177e4c Linux-2.6.12-rc2 |
1193 |
.id = "netem", |
10f6dfcfd Revert "sch_netem... |
1194 |
.cl_ops = &netem_class_ops, |
1da177e4c Linux-2.6.12-rc2 |
1195 1196 1197 |
.priv_size = sizeof(struct netem_sched_data), .enqueue = netem_enqueue, .dequeue = netem_dequeue, |
77be155cb pkt_sched: Add pe... |
1198 |
.peek = qdisc_peek_dequeued, |
1da177e4c 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 [NETEM]: Add vers... |
1210 1211 |
pr_info("netem: version " VERSION " "); |
1da177e4c 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"); |