Blame view
net/sched/sch_netem.c
27.3 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 |
/* * net/sched/sch_netem.c Network emulator * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version |
798b6b19d [PATCH] skge, sky... |
7 |
* 2 of the License. |
1da177e4c Linux-2.6.12-rc2 |
8 9 |
* * Many of the algorithms and ideas for this came from |
10297b993 [NET] SCHED: Fix ... |
10 |
* NIST Net which is not copyrighted. |
1da177e4c Linux-2.6.12-rc2 |
11 12 13 14 |
* * Authors: Stephen Hemminger <shemminger@osdl.org> * Catalin(ux aka Dino) BOIE <catab at umbrella dot ro> */ |
b7f080cfe net: remove mm.h ... |
15 |
#include <linux/mm.h> |
1da177e4c Linux-2.6.12-rc2 |
16 |
#include <linux/module.h> |
5a0e3ad6a include cleanup: ... |
17 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
18 19 20 |
#include <linux/types.h> #include <linux/kernel.h> #include <linux/errno.h> |
1da177e4c Linux-2.6.12-rc2 |
21 |
#include <linux/skbuff.h> |
78776d3f2 sch_netem: Need t... |
22 |
#include <linux/vmalloc.h> |
1da177e4c Linux-2.6.12-rc2 |
23 |
#include <linux/rtnetlink.h> |
90b41a1cd netem: add cell c... |
24 |
#include <linux/reciprocal_div.h> |
aec0a40a6 netem: use rb tre... |
25 |
#include <linux/rbtree.h> |
1da177e4c Linux-2.6.12-rc2 |
26 |
|
dc5fc579b [NETLINK]: Use nl... |
27 |
#include <net/netlink.h> |
1da177e4c Linux-2.6.12-rc2 |
28 |
#include <net/pkt_sched.h> |
e4ae004b8 netem: add ECN ca... |
29 |
#include <net/inet_ecn.h> |
1da177e4c Linux-2.6.12-rc2 |
30 |
|
250a65f78 netem: update ver... |
31 |
#define VERSION "1.3" |
eb229c4cd [NETEM]: Add vers... |
32 |
|
1da177e4c Linux-2.6.12-rc2 |
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
/* Network Emulation Queuing algorithm. ==================================== Sources: [1] Mark Carson, Darrin Santay, "NIST Net - A Linux-based Network Emulation Tool [2] Luigi Rizzo, DummyNet for FreeBSD ---------------------------------------------------------------- This started out as a simple way to delay outgoing packets to test TCP but has grown to include most of the functionality of a full blown network emulator like NISTnet. It can delay packets and add random jitter (and correlation). The random distribution can be loaded from a table as well to provide normal, Pareto, or experimental curves. Packet loss, duplication, and reordering can also be emulated. This qdisc does not do classification that can be handled in layering other disciplines. It does not need to do bandwidth control either since that can be handled by using token bucket or other rate control. |
661b79725 netem: revised co... |
54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
Correlated Loss Generator models Added generation of correlated loss according to the "Gilbert-Elliot" model, a 4-state markov model. References: [1] NetemCLG Home http://netgroup.uniroma2.it/NetemCLG [2] S. Salsano, F. Ludovici, A. Ordine, "Definition of a general and intuitive loss model for packet networks and its implementation in the Netem module in the Linux kernel", available in [1] Authors: Stefano Salsano <stefano.salsano at uniroma2.it Fabio Ludovici <fabio.ludovici at yahoo.it> |
1da177e4c Linux-2.6.12-rc2 |
68 69 70 |
*/ struct netem_sched_data { |
aec0a40a6 netem: use rb tre... |
71 72 |
/* internal t(ime)fifo qdisc uses t_root and sch->limit */ struct rb_root t_root; |
50612537e netem: fix classf... |
73 74 |
/* optional qdisc for classful handling (NULL at netem init) */ |
1da177e4c Linux-2.6.12-rc2 |
75 |
struct Qdisc *qdisc; |
50612537e netem: fix classf... |
76 |
|
59cb5c673 [NET_SCHED]: sch_... |
77 |
struct qdisc_watchdog watchdog; |
1da177e4c Linux-2.6.12-rc2 |
78 |
|
b407621c3 [NETEM]: use bett... |
79 80 |
psched_tdiff_t latency; psched_tdiff_t jitter; |
1da177e4c Linux-2.6.12-rc2 |
81 |
u32 loss; |
e4ae004b8 netem: add ECN ca... |
82 |
u32 ecn; |
1da177e4c Linux-2.6.12-rc2 |
83 84 85 |
u32 limit; u32 counter; u32 gap; |
1da177e4c Linux-2.6.12-rc2 |
86 |
u32 duplicate; |
0dca51d36 [PKT_SCHED] netem... |
87 |
u32 reorder; |
c865e5d99 [PKT_SCHED] netem... |
88 |
u32 corrupt; |
6a031f67c sch_netem: suppor... |
89 |
u64 rate; |
90b41a1cd netem: add cell c... |
90 91 |
s32 packet_overhead; u32 cell_size; |
809fa972f reciprocal_divide... |
92 |
struct reciprocal_value cell_size_reciprocal; |
90b41a1cd netem: add cell c... |
93 |
s32 cell_overhead; |
1da177e4c Linux-2.6.12-rc2 |
94 95 |
struct crndstate { |
b407621c3 [NETEM]: use bett... |
96 97 |
u32 last; u32 rho; |
c865e5d99 [PKT_SCHED] netem... |
98 |
} delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor; |
1da177e4c Linux-2.6.12-rc2 |
99 100 101 102 103 |
struct disttable { u32 size; s16 table[0]; } *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; |
1da177e4c Linux-2.6.12-rc2 |
132 |
}; |
50612537e netem: fix classf... |
133 134 |
/* Time stamp put into socket buffer control block * Only valid when skbs are in our internal t(ime)fifo queue. |
56b174256 net: add rbnode t... |
135 136 137 138 |
* * As skb->rbnode uses same storage than skb->next, skb->prev and skb->tstamp, * and skb->next & skb->prev are scratch space for a qdisc, * we save skb->tstamp value in skb->cb[] before destroying it. |
50612537e netem: fix classf... |
139 |
*/ |
1da177e4c Linux-2.6.12-rc2 |
140 141 |
struct netem_skb_cb { psched_time_t time_to_send; |
aec0a40a6 netem: use rb tre... |
142 |
ktime_t tstamp_save; |
1da177e4c Linux-2.6.12-rc2 |
143 |
}; |
5f86173bd net_sched: Add qd... |
144 145 |
static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { |
aec0a40a6 netem: use rb tre... |
146 |
/* we assume we can use skb next/prev/tstamp as storage for rb_node */ |
16bda13d9 net: Make qdisc_s... |
147 |
qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb)); |
175f9c1bb net_sched: Add si... |
148 |
return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; |
5f86173bd net_sched: Add qd... |
149 |
} |
1da177e4c Linux-2.6.12-rc2 |
150 151 152 153 154 155 |
/* init_crandom - initialize correlated random number generator * Use entropy source for initial seed. */ static void init_crandom(struct crndstate *state, unsigned long rho) { state->rho = rho; |
63862b5be net: replace macr... |
156 |
state->last = prandom_u32(); |
1da177e4c Linux-2.6.12-rc2 |
157 158 159 160 161 162 |
} /* get_crandom - correlated random number generator * Next number depends on last value. * rho is scaled to avoid floating point. */ |
b407621c3 [NETEM]: use bett... |
163 |
static u32 get_crandom(struct crndstate *state) |
1da177e4c Linux-2.6.12-rc2 |
164 165 166 |
{ u64 value, rho; unsigned long answer; |
bb2f8cc0e [NETEM]: spelling... |
167 |
if (state->rho == 0) /* no correlation */ |
63862b5be net: replace macr... |
168 |
return prandom_u32(); |
1da177e4c Linux-2.6.12-rc2 |
169 |
|
63862b5be net: replace macr... |
170 |
value = prandom_u32(); |
1da177e4c Linux-2.6.12-rc2 |
171 172 173 174 175 |
rho = (u64)state->rho + 1; answer = (value * ((1ull<<32) - rho) + state->last * rho) >> 32; state->last = answer; return answer; } |
661b79725 netem: revised co... |
176 177 178 179 180 181 182 |
/* loss_4state - 4-state model loss generator * Generates losses according to the 4-state Markov chain adopted in * the GI (General and Intuitive) loss model. */ static bool loss_4state(struct netem_sched_data *q) { struct clgstate *clg = &q->clg; |
63862b5be net: replace macr... |
183 |
u32 rnd = prandom_u32(); |
661b79725 netem: revised co... |
184 185 |
/* |
25985edce Fix common misspe... |
186 |
* Makes a comparison between rnd and the transition |
661b79725 netem: revised co... |
187 188 189 |
* probabilities outgoing from the current state, then decides the * next state and if the next packet has to be transmitted or lost. * The four states correspond to: |
a6e2fe17e sch_netem: replac... |
190 191 192 193 |
* TX_IN_GAP_PERIOD => successfully transmitted packets within a gap period * LOST_IN_BURST_PERIOD => isolated losses within a gap period * LOST_IN_GAP_PERIOD => lost packets within a burst period * TX_IN_GAP_PERIOD => successfully transmitted packets within a burst period |
661b79725 netem: revised co... |
194 195 |
*/ switch (clg->state) { |
a6e2fe17e sch_netem: replac... |
196 |
case TX_IN_GAP_PERIOD: |
661b79725 netem: revised co... |
197 |
if (rnd < clg->a4) { |
a6e2fe17e sch_netem: replac... |
198 |
clg->state = LOST_IN_BURST_PERIOD; |
661b79725 netem: revised co... |
199 |
return true; |
ab6c27be8 netem: fix loss 4... |
200 |
} else if (clg->a4 < rnd && rnd < clg->a1 + clg->a4) { |
a6e2fe17e sch_netem: replac... |
201 |
clg->state = LOST_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
202 |
return true; |
a6e2fe17e sch_netem: replac... |
203 204 205 |
} else if (clg->a1 + clg->a4 < rnd) { clg->state = TX_IN_GAP_PERIOD; } |
661b79725 netem: revised co... |
206 207 |
break; |
a6e2fe17e sch_netem: replac... |
208 |
case TX_IN_BURST_PERIOD: |
661b79725 netem: revised co... |
209 |
if (rnd < clg->a5) { |
a6e2fe17e sch_netem: replac... |
210 |
clg->state = LOST_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
211 |
return true; |
a6e2fe17e sch_netem: replac... |
212 213 214 |
} else { clg->state = TX_IN_BURST_PERIOD; } |
661b79725 netem: revised co... |
215 216 |
break; |
a6e2fe17e sch_netem: replac... |
217 |
case LOST_IN_GAP_PERIOD: |
661b79725 netem: revised co... |
218 |
if (rnd < clg->a3) |
a6e2fe17e sch_netem: replac... |
219 |
clg->state = TX_IN_BURST_PERIOD; |
661b79725 netem: revised co... |
220 |
else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) { |
a6e2fe17e sch_netem: replac... |
221 |
clg->state = TX_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
222 |
} else if (clg->a2 + clg->a3 < rnd) { |
a6e2fe17e sch_netem: replac... |
223 |
clg->state = LOST_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
224 225 226 |
return true; } break; |
a6e2fe17e sch_netem: replac... |
227 228 |
case LOST_IN_BURST_PERIOD: clg->state = TX_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
229 230 231 232 233 234 235 236 237 238 |
break; } return false; } /* loss_gilb_ell - Gilbert-Elliot model loss generator * Generates losses according to the Gilbert-Elliot loss model or * its special cases (Gilbert or Simple Gilbert) * |
25985edce Fix common misspe... |
239 |
* Makes a comparison between random number and the transition |
661b79725 netem: revised co... |
240 |
* probabilities outgoing from the current state, then decides the |
25985edce Fix common misspe... |
241 |
* next state. A second random number is extracted and the comparison |
661b79725 netem: revised co... |
242 243 244 245 246 247 248 249 |
* with the loss probability of the current state decides if the next * packet will be transmitted or lost. */ static bool loss_gilb_ell(struct netem_sched_data *q) { struct clgstate *clg = &q->clg; switch (clg->state) { |
c045a734d sch_netem: replac... |
250 |
case GOOD_STATE: |
63862b5be net: replace macr... |
251 |
if (prandom_u32() < clg->a1) |
c045a734d sch_netem: replac... |
252 |
clg->state = BAD_STATE; |
63862b5be net: replace macr... |
253 |
if (prandom_u32() < clg->a4) |
661b79725 netem: revised co... |
254 |
return true; |
7c2781fa9 netem: missing br... |
255 |
break; |
c045a734d sch_netem: replac... |
256 |
case BAD_STATE: |
63862b5be net: replace macr... |
257 |
if (prandom_u32() < clg->a2) |
c045a734d sch_netem: replac... |
258 |
clg->state = GOOD_STATE; |
63862b5be net: replace macr... |
259 |
if (prandom_u32() > clg->a3) |
661b79725 netem: revised co... |
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
return true; } return false; } static bool loss_event(struct netem_sched_data *q) { switch (q->loss_model) { case CLG_RANDOM: /* Random packet drop 0 => none, ~0 => all */ return q->loss && q->loss >= get_crandom(&q->loss_cor); case CLG_4_STATES: /* 4state loss model algorithm (used also for GI model) * Extracts a value from the markov 4 state loss generator, * if it is 1 drops a packet and if needed writes the event in * the kernel logs */ return loss_4state(q); case CLG_GILB_ELL: /* Gilbert-Elliot loss model algorithm * Extracts a value from the Gilbert-Elliot loss generator, * if it is 1 drops a packet and if needed writes the event in * the kernel logs */ return loss_gilb_ell(q); } return false; /* not reached */ } |
1da177e4c Linux-2.6.12-rc2 |
292 293 294 295 |
/* tabledist - return a pseudo-randomly distributed value with mean mu and * std deviation sigma. Uses table lookup to approximate the desired * distribution, and a uniformly-distributed pseudo-random source. */ |
b407621c3 [NETEM]: use bett... |
296 297 298 |
static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma, struct crndstate *state, const struct disttable *dist) |
1da177e4c Linux-2.6.12-rc2 |
299 |
{ |
b407621c3 [NETEM]: use bett... |
300 301 302 |
psched_tdiff_t x; long t; u32 rnd; |
1da177e4c Linux-2.6.12-rc2 |
303 304 305 306 307 308 309 |
if (sigma == 0) return mu; rnd = get_crandom(state); /* default uniform distribution */ |
10297b993 [NET] SCHED: Fix ... |
310 |
if (dist == NULL) |
1da177e4c Linux-2.6.12-rc2 |
311 312 313 314 315 316 317 318 319 320 321 |
return (rnd % (2*sigma)) - sigma + mu; t = dist->table[rnd % dist->size]; x = (sigma % NETEM_DIST_SCALE) * t; if (x >= 0) x += NETEM_DIST_SCALE/2; else x -= NETEM_DIST_SCALE/2; return x / NETEM_DIST_SCALE + (sigma / NETEM_DIST_SCALE) * t + mu; } |
90b41a1cd netem: add cell c... |
322 |
static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sched_data *q) |
7bc0f28c7 netem: rate exten... |
323 |
{ |
90b41a1cd netem: add cell c... |
324 |
u64 ticks; |
fc33cc724 netem: fix build ... |
325 |
|
90b41a1cd netem: add cell c... |
326 327 328 329 330 331 332 333 334 335 336 337 338 |
len += q->packet_overhead; if (q->cell_size) { u32 cells = reciprocal_divide(len, q->cell_size_reciprocal); if (len > cells * q->cell_size) /* extra cell needed for remainder */ cells++; len = cells * (q->cell_size + q->cell_overhead); } ticks = (u64)len * NSEC_PER_SEC; do_div(ticks, q->rate); |
fc33cc724 netem: fix build ... |
339 |
return PSCHED_NS2TICKS(ticks); |
7bc0f28c7 netem: rate exten... |
340 |
} |
ff704050f netem: free skb's... |
341 342 343 344 345 346 |
static void tfifo_reset(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); struct rb_node *p; while ((p = rb_first(&q->t_root))) { |
37c7cc80b net: add rb_to_sk... |
347 |
struct sk_buff *skb = rb_to_skb(p); |
ff704050f netem: free skb's... |
348 349 |
rb_erase(p, &q->t_root); |
2f08a9a16 net_sched: sch_ne... |
350 |
rtnl_kfree_skbs(skb, skb); |
ff704050f netem: free skb's... |
351 352 |
} } |
960fb66e5 netem: add limita... |
353 |
static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) |
50612537e netem: fix classf... |
354 |
{ |
aec0a40a6 netem: use rb tre... |
355 |
struct netem_sched_data *q = qdisc_priv(sch); |
50612537e netem: fix classf... |
356 |
psched_time_t tnext = netem_skb_cb(nskb)->time_to_send; |
aec0a40a6 netem: use rb tre... |
357 |
struct rb_node **p = &q->t_root.rb_node, *parent = NULL; |
50612537e netem: fix classf... |
358 |
|
aec0a40a6 netem: use rb tre... |
359 360 |
while (*p) { struct sk_buff *skb; |
50612537e netem: fix classf... |
361 |
|
aec0a40a6 netem: use rb tre... |
362 |
parent = *p; |
37c7cc80b net: add rb_to_sk... |
363 |
skb = rb_to_skb(parent); |
960fb66e5 netem: add limita... |
364 |
if (tnext >= netem_skb_cb(skb)->time_to_send) |
aec0a40a6 netem: use rb tre... |
365 366 367 |
p = &parent->rb_right; else p = &parent->rb_left; |
50612537e netem: fix classf... |
368 |
} |
56b174256 net: add rbnode t... |
369 370 |
rb_link_node(&nskb->rbnode, parent, p); rb_insert_color(&nskb->rbnode, &q->t_root); |
aec0a40a6 netem: use rb tre... |
371 |
sch->q.qlen++; |
50612537e netem: fix classf... |
372 |
} |
6071bd1aa netem: Segment GS... |
373 374 375 376 |
/* netem can't properly corrupt a megapacket (like we get from GSO), so instead * when we statistically choose to corrupt one, we instead segment it, returning * the first packet to be corrupted, and re-enqueue the remaining frames */ |
520ac30f4 net_sched: drop p... |
377 378 |
static struct sk_buff *netem_segment(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) |
6071bd1aa netem: Segment GS... |
379 380 381 382 383 384 385 |
{ struct sk_buff *segs; netdev_features_t features = netif_skb_features(skb); segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); if (IS_ERR_OR_NULL(segs)) { |
520ac30f4 net_sched: drop p... |
386 |
qdisc_drop(skb, sch, to_free); |
6071bd1aa netem: Segment GS... |
387 388 389 390 391 |
return NULL; } consume_skb(skb); return segs; } |
48da34b7a sched: add and us... |
392 393 394 395 396 397 398 399 400 |
static void netem_enqueue_skb_head(struct qdisc_skb_head *qh, struct sk_buff *skb) { skb->next = qh->head; if (!qh->head) qh->tail = skb; qh->head = skb; qh->qlen++; } |
0afb51e72 [PKT_SCHED]: nete... |
401 402 403 404 405 406 |
/* * Insert one skb into qdisc. * Note: parent depends on return value to account for queue length. * NET_XMIT_DROP: queue length didn't change. * NET_XMIT_SUCCESS: one skb was queued. */ |
520ac30f4 net_sched: drop p... |
407 408 |
static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) |
1da177e4c Linux-2.6.12-rc2 |
409 410 |
{ struct netem_sched_data *q = qdisc_priv(sch); |
89e1df74f [PKT_SCHED] netem... |
411 412 |
/* We don't fill cb now as skb_unshare() may invalidate it */ struct netem_skb_cb *cb; |
0afb51e72 [PKT_SCHED]: nete... |
413 |
struct sk_buff *skb2; |
6071bd1aa netem: Segment GS... |
414 415 416 |
struct sk_buff *segs = NULL; unsigned int len = 0, last_len, prev_len = qdisc_pkt_len(skb); int nb = 0; |
0afb51e72 [PKT_SCHED]: nete... |
417 |
int count = 1; |
6071bd1aa netem: Segment GS... |
418 |
int rc = NET_XMIT_SUCCESS; |
1da177e4c Linux-2.6.12-rc2 |
419 |
|
0afb51e72 [PKT_SCHED]: nete... |
420 421 422 |
/* Random duplication */ if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)) ++count; |
661b79725 netem: revised co... |
423 |
/* Drop packet? */ |
e4ae004b8 netem: add ECN ca... |
424 425 |
if (loss_event(q)) { if (q->ecn && INET_ECN_set_ce(skb)) |
25331d6ce net: sched: imple... |
426 |
qdisc_qstats_drop(sch); /* mark packet */ |
e4ae004b8 netem: add ECN ca... |
427 428 429 |
else --count; } |
0afb51e72 [PKT_SCHED]: nete... |
430 |
if (count == 0) { |
25331d6ce net: sched: imple... |
431 |
qdisc_qstats_drop(sch); |
520ac30f4 net_sched: drop p... |
432 |
__qdisc_drop(skb, to_free); |
c27f339af net_sched: Add qd... |
433 |
return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; |
1da177e4c Linux-2.6.12-rc2 |
434 |
} |
5a308f40b netem: refine ear... |
435 436 |
/* If a delay is expected, orphan the skb. (orphaning usually takes * place at TX completion time, so _before_ the link transit delay) |
5a308f40b netem: refine ear... |
437 |
*/ |
5080f39e8 netem: apply corr... |
438 |
if (q->latency || q->jitter || q->rate) |
f2f872f92 netem: Introduce ... |
439 |
skb_orphan_partial(skb); |
4e8a52015 [PKT_SCHED] netem... |
440 |
|
0afb51e72 [PKT_SCHED]: nete... |
441 442 443 444 445 446 |
/* * If we need to duplicate packet, then re-insert at top of the * qdisc tree, since parent queuer expects that only one * skb will be queued. */ if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { |
7698b4fca pkt_sched: Add an... |
447 |
struct Qdisc *rootq = qdisc_root(sch); |
0afb51e72 [PKT_SCHED]: nete... |
448 |
u32 dupsave = q->duplicate; /* prevent duplicating a dup... */ |
0afb51e72 [PKT_SCHED]: nete... |
449 |
|
b396cca6f net: sched: depre... |
450 |
q->duplicate = 0; |
520ac30f4 net_sched: drop p... |
451 |
rootq->enqueue(skb2, rootq, to_free); |
0afb51e72 [PKT_SCHED]: nete... |
452 |
q->duplicate = dupsave; |
1da177e4c Linux-2.6.12-rc2 |
453 |
} |
c865e5d99 [PKT_SCHED] netem... |
454 455 456 457 458 459 460 |
/* * Randomized packet corruption. * Make copy if needed since we are modifying * If packet is going to be hardware checksummed, then * do it now in software before we mangle it. */ if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) { |
6071bd1aa netem: Segment GS... |
461 |
if (skb_is_gso(skb)) { |
520ac30f4 net_sched: drop p... |
462 |
segs = netem_segment(skb, sch, to_free); |
6071bd1aa netem: Segment GS... |
463 464 465 466 467 468 469 470 |
if (!segs) return NET_XMIT_DROP; } else { segs = skb; } skb = segs; segs = segs->next; |
8a6e9c670 net_sched: netem:... |
471 472 473 474 475 476 477 478 |
skb = skb_unshare(skb, GFP_ATOMIC); if (unlikely(!skb)) { qdisc_qstats_drop(sch); goto finish_segs; } if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { qdisc_drop(skb, sch, to_free); |
6071bd1aa netem: Segment GS... |
479 480 |
goto finish_segs; } |
c865e5d99 [PKT_SCHED] netem... |
481 |
|
63862b5be net: replace macr... |
482 483 |
skb->data[prandom_u32() % skb_headlen(skb)] ^= 1<<(prandom_u32() % 8); |
c865e5d99 [PKT_SCHED] netem... |
484 |
} |
97d0678f9 sched: don't use ... |
485 |
if (unlikely(sch->q.qlen >= sch->limit)) |
f77ff13a0 sch_netem: fix sk... |
486 |
return qdisc_drop_all(skb, sch, to_free); |
960fb66e5 netem: add limita... |
487 |
|
25331d6ce net: sched: imple... |
488 |
qdisc_qstats_backlog_inc(sch, skb); |
960fb66e5 netem: add limita... |
489 |
|
5f86173bd net_sched: Add qd... |
490 |
cb = netem_skb_cb(skb); |
cc7ec456f net_sched: cleanups |
491 |
if (q->gap == 0 || /* not doing reordering */ |
a42b4799c netem: Fix off-by... |
492 |
q->counter < q->gap - 1 || /* inside last reordering gap */ |
f64f9e719 net: Move && and ... |
493 |
q->reorder < get_crandom(&q->reorder_cor)) { |
0f9f32ac6 [PKT_SCHED] netem... |
494 |
psched_time_t now; |
07aaa1154 [NETEM]: use PSCH... |
495 496 497 498 |
psched_tdiff_t delay; delay = tabledist(q->latency, q->jitter, &q->delay_cor, q->delay_dist); |
3bebcda28 [NET_SCHED]: turn... |
499 |
now = psched_get_time(); |
7bc0f28c7 netem: rate exten... |
500 501 |
if (q->rate) { |
5080f39e8 netem: apply corr... |
502 503 504 505 506 507 508 |
struct netem_skb_cb *last = NULL; if (sch->q.tail) last = netem_skb_cb(sch->q.tail); if (q->t_root.rb_node) { struct sk_buff *t_skb; struct netem_skb_cb *t_last; |
37c7cc80b net: add rb_to_sk... |
509 |
t_skb = skb_rb_last(&q->t_root); |
5080f39e8 netem: apply corr... |
510 511 512 513 514 515 |
t_last = netem_skb_cb(t_skb); if (!last || t_last->time_to_send > last->time_to_send) { last = t_last; } } |
7bc0f28c7 netem: rate exten... |
516 |
|
aec0a40a6 netem: use rb tre... |
517 |
if (last) { |
7bc0f28c7 netem: rate exten... |
518 |
/* |
a13d31047 netem: fix delay ... |
519 520 |
* Last packet in queue is reference point (now), * calculate this time bonus and subtract |
7bc0f28c7 netem: rate exten... |
521 522 |
* from delay. */ |
5080f39e8 netem: apply corr... |
523 |
delay -= last->time_to_send - now; |
a13d31047 netem: fix delay ... |
524 |
delay = max_t(psched_tdiff_t, 0, delay); |
5080f39e8 netem: apply corr... |
525 |
now = last->time_to_send; |
7bc0f28c7 netem: rate exten... |
526 |
} |
a13d31047 netem: fix delay ... |
527 |
|
8cfd88d6d sch_netem: more p... |
528 |
delay += packet_len_2_sched_time(qdisc_pkt_len(skb), q); |
7bc0f28c7 netem: rate exten... |
529 |
} |
7c59e25f3 [NET_SCHED]: kill... |
530 |
cb->time_to_send = now + delay; |
aec0a40a6 netem: use rb tre... |
531 |
cb->tstamp_save = skb->tstamp; |
1da177e4c Linux-2.6.12-rc2 |
532 |
++q->counter; |
960fb66e5 netem: add limita... |
533 |
tfifo_enqueue(skb, sch); |
1da177e4c Linux-2.6.12-rc2 |
534 |
} else { |
10297b993 [NET] SCHED: Fix ... |
535 |
/* |
0dca51d36 [PKT_SCHED] netem... |
536 537 538 |
* Do re-ordering by putting one out of N packets at the front * of the queue. */ |
3bebcda28 [NET_SCHED]: turn... |
539 |
cb->time_to_send = psched_get_time(); |
0dca51d36 [PKT_SCHED] netem... |
540 |
q->counter = 0; |
8ba25dad0 sch_netem: Replac... |
541 |
|
48da34b7a sched: add and us... |
542 |
netem_enqueue_skb_head(&sch->q, skb); |
eb1019244 net_sched: Bug in... |
543 |
sch->qstats.requeues++; |
378a2f090 net_sched: Add qd... |
544 |
} |
1da177e4c Linux-2.6.12-rc2 |
545 |
|
6071bd1aa netem: Segment GS... |
546 547 548 549 550 551 552 |
finish_segs: if (segs) { while (segs) { skb2 = segs->next; segs->next = NULL; qdisc_skb_cb(segs)->pkt_len = segs->len; last_len = segs->len; |
520ac30f4 net_sched: drop p... |
553 |
rc = qdisc_enqueue(segs, sch, to_free); |
6071bd1aa netem: Segment GS... |
554 555 556 557 558 559 560 561 562 563 564 565 566 |
if (rc != NET_XMIT_SUCCESS) { if (net_xmit_drop_count(rc)) qdisc_qstats_drop(sch); } else { nb++; len += last_len; } segs = skb2; } sch->q.qlen += nb; if (nb > 1) qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len); } |
10f6dfcfd Revert "sch_netem... |
567 |
return NET_XMIT_SUCCESS; |
1da177e4c Linux-2.6.12-rc2 |
568 |
} |
1da177e4c Linux-2.6.12-rc2 |
569 570 571 572 |
static struct sk_buff *netem_dequeue(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; |
aec0a40a6 netem: use rb tre... |
573 |
struct rb_node *p; |
1da177e4c Linux-2.6.12-rc2 |
574 |
|
50612537e netem: fix classf... |
575 |
tfifo_dequeue: |
ed760cb8a sched: replace __... |
576 |
skb = __qdisc_dequeue_head(&sch->q); |
771018e76 [PKT_SCHED]: nete... |
577 |
if (skb) { |
25331d6ce net: sched: imple... |
578 |
qdisc_qstats_backlog_dec(sch, skb); |
0ad2a8365 netem: Fixes byte... |
579 |
deliver: |
aec0a40a6 netem: use rb tre... |
580 581 582 583 584 |
qdisc_bstats_update(sch, skb); return skb; } p = rb_first(&q->t_root); if (p) { |
36b7bfe09 netem: fix possib... |
585 |
psched_time_t time_to_send; |
37c7cc80b net: add rb_to_sk... |
586 |
skb = rb_to_skb(p); |
0f9f32ac6 [PKT_SCHED] netem... |
587 588 |
/* if more time remaining? */ |
36b7bfe09 netem: fix possib... |
589 590 |
time_to_send = netem_skb_cb(skb)->time_to_send; if (time_to_send <= psched_get_time()) { |
aec0a40a6 netem: use rb tre... |
591 592 593 |
rb_erase(p, &q->t_root); sch->q.qlen--; |
0ad2a8365 netem: Fixes byte... |
594 |
qdisc_qstats_backlog_dec(sch, skb); |
aec0a40a6 netem: use rb tre... |
595 596 597 |
skb->next = NULL; skb->prev = NULL; skb->tstamp = netem_skb_cb(skb)->tstamp_save; |
03c05f0d4 pkt_sched: Use qd... |
598 |
|
8caf15397 net: sch_netem: F... |
599 600 601 602 603 |
#ifdef CONFIG_NET_CLS_ACT /* * If it's at ingress let's pretend the delay is * from the network (tstamp will be updated). */ |
bc31c905e net-tc: convert t... |
604 |
if (skb->tc_redirected && skb->tc_from_ingress) |
2456e8553 ktime: Get rid of... |
605 |
skb->tstamp = 0; |
8caf15397 net: sch_netem: F... |
606 |
#endif |
10f6dfcfd Revert "sch_netem... |
607 |
|
50612537e netem: fix classf... |
608 |
if (q->qdisc) { |
21de12ee5 netem: fix a use ... |
609 |
unsigned int pkt_len = qdisc_pkt_len(skb); |
520ac30f4 net_sched: drop p... |
610 611 |
struct sk_buff *to_free = NULL; int err; |
50612537e netem: fix classf... |
612 |
|
520ac30f4 net_sched: drop p... |
613 614 |
err = qdisc_enqueue(skb, q->qdisc, &to_free); kfree_skb_list(to_free); |
21de12ee5 netem: fix a use ... |
615 616 617 618 619 |
if (err != NET_XMIT_SUCCESS && net_xmit_drop_count(err)) { qdisc_qstats_drop(sch); qdisc_tree_reduce_backlog(sch, 1, pkt_len); |
50612537e netem: fix classf... |
620 621 622 |
} goto tfifo_dequeue; } |
aec0a40a6 netem: use rb tre... |
623 |
goto deliver; |
07aaa1154 [NETEM]: use PSCH... |
624 |
} |
11274e5a4 [NETEM]: avoid ex... |
625 |
|
50612537e netem: fix classf... |
626 627 628 629 630 |
if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) goto deliver; } |
36b7bfe09 netem: fix possib... |
631 |
qdisc_watchdog_schedule(&q->watchdog, time_to_send); |
0f9f32ac6 [PKT_SCHED] netem... |
632 |
} |
50612537e netem: fix classf... |
633 634 635 636 637 |
if (q->qdisc) { skb = q->qdisc->ops->dequeue(q->qdisc); if (skb) goto deliver; } |
0f9f32ac6 [PKT_SCHED] netem... |
638 |
return NULL; |
1da177e4c Linux-2.6.12-rc2 |
639 |
} |
1da177e4c Linux-2.6.12-rc2 |
640 641 642 |
static void netem_reset(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); |
50612537e netem: fix classf... |
643 |
qdisc_reset_queue(sch); |
ff704050f netem: free skb's... |
644 |
tfifo_reset(sch); |
50612537e netem: fix classf... |
645 646 |
if (q->qdisc) qdisc_reset(q->qdisc); |
59cb5c673 [NET_SCHED]: sch_... |
647 |
qdisc_watchdog_cancel(&q->watchdog); |
1da177e4c Linux-2.6.12-rc2 |
648 |
} |
6373a9a28 netem: use vmallo... |
649 650 |
static void dist_free(struct disttable *d) { |
4cb28970a net: use the new ... |
651 |
kvfree(d); |
6373a9a28 netem: use vmallo... |
652 |
} |
1da177e4c Linux-2.6.12-rc2 |
653 654 655 656 |
/* * Distribution data is a variable size payload containing * signed 16 bit values. */ |
1e90474c3 [NET_SCHED]: Conv... |
657 |
static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr) |
1da177e4c Linux-2.6.12-rc2 |
658 659 |
{ struct netem_sched_data *q = qdisc_priv(sch); |
6373a9a28 netem: use vmallo... |
660 |
size_t n = nla_len(attr)/sizeof(__s16); |
1e90474c3 [NET_SCHED]: Conv... |
661 |
const __s16 *data = nla_data(attr); |
7698b4fca pkt_sched: Add an... |
662 |
spinlock_t *root_lock; |
1da177e4c Linux-2.6.12-rc2 |
663 664 |
struct disttable *d; int i; |
df173bda2 netem: define NET... |
665 |
if (n > NETEM_DIST_MAX) |
1da177e4c Linux-2.6.12-rc2 |
666 |
return -EINVAL; |
752ade68c treewide: use kv[... |
667 |
d = kvmalloc(sizeof(struct disttable) + n * sizeof(s16), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
668 669 670 671 672 673 |
if (!d) return -ENOMEM; d->size = n; for (i = 0; i < n; i++) d->table[i] = data[i]; |
10297b993 [NET] SCHED: Fix ... |
674 |
|
102396ae6 pkt_sched: Fix lo... |
675 |
root_lock = qdisc_root_sleeping_lock(sch); |
7698b4fca pkt_sched: Add an... |
676 677 |
spin_lock_bh(root_lock); |
bb52c7acf netem: dont call ... |
678 |
swap(q->delay_dist, d); |
7698b4fca pkt_sched: Add an... |
679 |
spin_unlock_bh(root_lock); |
bb52c7acf netem: dont call ... |
680 681 |
dist_free(d); |
1da177e4c Linux-2.6.12-rc2 |
682 683 |
return 0; } |
49545a777 sch_netem: change... |
684 |
static void get_correlation(struct netem_sched_data *q, const struct nlattr *attr) |
1da177e4c Linux-2.6.12-rc2 |
685 |
{ |
1e90474c3 [NET_SCHED]: Conv... |
686 |
const struct tc_netem_corr *c = nla_data(attr); |
1da177e4c Linux-2.6.12-rc2 |
687 |
|
1da177e4c Linux-2.6.12-rc2 |
688 689 690 |
init_crandom(&q->delay_cor, c->delay_corr); init_crandom(&q->loss_cor, c->loss_corr); init_crandom(&q->dup_cor, c->dup_corr); |
1da177e4c Linux-2.6.12-rc2 |
691 |
} |
49545a777 sch_netem: change... |
692 |
static void get_reorder(struct netem_sched_data *q, const struct nlattr *attr) |
0dca51d36 [PKT_SCHED] netem... |
693 |
{ |
1e90474c3 [NET_SCHED]: Conv... |
694 |
const struct tc_netem_reorder *r = nla_data(attr); |
0dca51d36 [PKT_SCHED] netem... |
695 |
|
0dca51d36 [PKT_SCHED] netem... |
696 697 |
q->reorder = r->probability; init_crandom(&q->reorder_cor, r->correlation); |
0dca51d36 [PKT_SCHED] netem... |
698 |
} |
49545a777 sch_netem: change... |
699 |
static void get_corrupt(struct netem_sched_data *q, const struct nlattr *attr) |
c865e5d99 [PKT_SCHED] netem... |
700 |
{ |
1e90474c3 [NET_SCHED]: Conv... |
701 |
const struct tc_netem_corrupt *r = nla_data(attr); |
c865e5d99 [PKT_SCHED] netem... |
702 |
|
c865e5d99 [PKT_SCHED] netem... |
703 704 |
q->corrupt = r->probability; init_crandom(&q->corrupt_cor, r->correlation); |
c865e5d99 [PKT_SCHED] netem... |
705 |
} |
49545a777 sch_netem: change... |
706 |
static void get_rate(struct netem_sched_data *q, const struct nlattr *attr) |
7bc0f28c7 netem: rate exten... |
707 |
{ |
7bc0f28c7 netem: rate exten... |
708 709 710 |
const struct tc_netem_rate *r = nla_data(attr); q->rate = r->rate; |
90b41a1cd netem: add cell c... |
711 712 |
q->packet_overhead = r->packet_overhead; q->cell_size = r->cell_size; |
809fa972f reciprocal_divide... |
713 |
q->cell_overhead = r->cell_overhead; |
90b41a1cd netem: add cell c... |
714 715 |
if (q->cell_size) q->cell_size_reciprocal = reciprocal_value(q->cell_size); |
809fa972f reciprocal_divide... |
716 717 |
else q->cell_size_reciprocal = (struct reciprocal_value) { 0 }; |
7bc0f28c7 netem: rate exten... |
718 |
} |
49545a777 sch_netem: change... |
719 |
static int get_loss_clg(struct netem_sched_data *q, const struct nlattr *attr) |
661b79725 netem: revised co... |
720 |
{ |
661b79725 netem: revised co... |
721 722 723 724 725 |
const struct nlattr *la; int rem; nla_for_each_nested(la, attr, rem) { u16 type = nla_type(la); |
833fa7438 net_sched: add sp... |
726 |
switch (type) { |
661b79725 netem: revised co... |
727 728 |
case NETEM_LOSS_GI: { const struct tc_netem_gimodel *gi = nla_data(la); |
2494654d4 netem: loss model... |
729 |
if (nla_len(la) < sizeof(struct tc_netem_gimodel)) { |
661b79725 netem: revised co... |
730 731 732 733 734 735 |
pr_info("netem: incorrect gi model size "); return -EINVAL; } q->loss_model = CLG_4_STATES; |
3fbac2a87 sch_netem: replac... |
736 |
q->clg.state = TX_IN_GAP_PERIOD; |
661b79725 netem: revised co... |
737 738 739 740 741 742 743 744 745 746 |
q->clg.a1 = gi->p13; q->clg.a2 = gi->p31; q->clg.a3 = gi->p32; q->clg.a4 = gi->p14; q->clg.a5 = gi->p23; break; } case NETEM_LOSS_GE: { const struct tc_netem_gemodel *ge = nla_data(la); |
2494654d4 netem: loss model... |
747 748 749 |
if (nla_len(la) < sizeof(struct tc_netem_gemodel)) { pr_info("netem: incorrect ge model size "); |
661b79725 netem: revised co... |
750 751 752 753 |
return -EINVAL; } q->loss_model = CLG_GILB_ELL; |
3fbac2a87 sch_netem: replac... |
754 |
q->clg.state = GOOD_STATE; |
661b79725 netem: revised co... |
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 |
q->clg.a1 = ge->p; q->clg.a2 = ge->r; q->clg.a3 = ge->h; q->clg.a4 = ge->k1; break; } default: pr_info("netem: unknown loss type %u ", type); return -EINVAL; } } return 0; } |
27a3421e4 [NET_SCHED]: Use ... |
771 772 773 774 |
static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = { [TCA_NETEM_CORR] = { .len = sizeof(struct tc_netem_corr) }, [TCA_NETEM_REORDER] = { .len = sizeof(struct tc_netem_reorder) }, [TCA_NETEM_CORRUPT] = { .len = sizeof(struct tc_netem_corrupt) }, |
7bc0f28c7 netem: rate exten... |
775 |
[TCA_NETEM_RATE] = { .len = sizeof(struct tc_netem_rate) }, |
661b79725 netem: revised co... |
776 |
[TCA_NETEM_LOSS] = { .type = NLA_NESTED }, |
e4ae004b8 netem: add ECN ca... |
777 |
[TCA_NETEM_ECN] = { .type = NLA_U32 }, |
6a031f67c sch_netem: suppor... |
778 |
[TCA_NETEM_RATE64] = { .type = NLA_U64 }, |
27a3421e4 [NET_SCHED]: Use ... |
779 |
}; |
2c10b32bf netlink: Remove c... |
780 781 782 783 |
static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, const struct nla_policy *policy, int len) { int nested_len = nla_len(nla) - NLA_ALIGN(len); |
661b79725 netem: revised co... |
784 785 786 |
if (nested_len < 0) { pr_info("netem: invalid attributes len %d ", nested_len); |
2c10b32bf netlink: Remove c... |
787 |
return -EINVAL; |
661b79725 netem: revised co... |
788 |
} |
2c10b32bf netlink: Remove c... |
789 790 |
if (nested_len >= nla_attr_size(0)) return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), |
fceb6435e netlink: pass ext... |
791 |
nested_len, policy, NULL); |
661b79725 netem: revised co... |
792 |
|
2c10b32bf netlink: Remove c... |
793 794 795 |
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); return 0; } |
c865e5d99 [PKT_SCHED] netem... |
796 |
/* Parse netlink message to set options */ |
1e90474c3 [NET_SCHED]: Conv... |
797 |
static int netem_change(struct Qdisc *sch, struct nlattr *opt) |
1da177e4c Linux-2.6.12-rc2 |
798 799 |
{ struct netem_sched_data *q = qdisc_priv(sch); |
b03f46720 [NET_SCHED]: sch_... |
800 |
struct nlattr *tb[TCA_NETEM_MAX + 1]; |
1da177e4c Linux-2.6.12-rc2 |
801 |
struct tc_netem_qopt *qopt; |
54a4b05cd sch_netem: return... |
802 803 |
struct clgstate old_clg; int old_loss_model = CLG_RANDOM; |
1da177e4c Linux-2.6.12-rc2 |
804 |
int ret; |
10297b993 [NET] SCHED: Fix ... |
805 |
|
b03f46720 [NET_SCHED]: sch_... |
806 |
if (opt == NULL) |
1da177e4c Linux-2.6.12-rc2 |
807 |
return -EINVAL; |
2c10b32bf netlink: Remove c... |
808 809 |
qopt = nla_data(opt); ret = parse_attr(tb, TCA_NETEM_MAX, opt, netem_policy, sizeof(*qopt)); |
b03f46720 [NET_SCHED]: sch_... |
810 811 |
if (ret < 0) return ret; |
54a4b05cd sch_netem: return... |
812 813 814 815 816 |
/* backup q->clg and q->loss_model */ old_clg = q->clg; old_loss_model = q->loss_model; if (tb[TCA_NETEM_LOSS]) { |
49545a777 sch_netem: change... |
817 |
ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]); |
54a4b05cd sch_netem: return... |
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 |
if (ret) { q->loss_model = old_loss_model; return ret; } } else { q->loss_model = CLG_RANDOM; } if (tb[TCA_NETEM_DELAY_DIST]) { ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]); if (ret) { /* recover clg and loss_model, in case of * q->clg and q->loss_model were modified * in get_loss_clg() */ q->clg = old_clg; q->loss_model = old_loss_model; return ret; } } |
50612537e netem: fix classf... |
838 |
sch->limit = qopt->limit; |
10297b993 [NET] SCHED: Fix ... |
839 |
|
1da177e4c Linux-2.6.12-rc2 |
840 841 842 843 |
q->latency = qopt->latency; q->jitter = qopt->jitter; q->limit = qopt->limit; q->gap = qopt->gap; |
0dca51d36 [PKT_SCHED] netem... |
844 |
q->counter = 0; |
1da177e4c Linux-2.6.12-rc2 |
845 846 |
q->loss = qopt->loss; q->duplicate = qopt->duplicate; |
bb2f8cc0e [NETEM]: spelling... |
847 848 |
/* for compatibility with earlier versions. * if gap is set, need to assume 100% probability |
0dca51d36 [PKT_SCHED] netem... |
849 |
*/ |
a362e0a78 [NETEM]: report r... |
850 851 |
if (q->gap) q->reorder = ~0; |
0dca51d36 [PKT_SCHED] netem... |
852 |
|
265eb67fb netem: eliminate ... |
853 |
if (tb[TCA_NETEM_CORR]) |
49545a777 sch_netem: change... |
854 |
get_correlation(q, tb[TCA_NETEM_CORR]); |
1da177e4c Linux-2.6.12-rc2 |
855 |
|
265eb67fb netem: eliminate ... |
856 |
if (tb[TCA_NETEM_REORDER]) |
49545a777 sch_netem: change... |
857 |
get_reorder(q, tb[TCA_NETEM_REORDER]); |
1da177e4c Linux-2.6.12-rc2 |
858 |
|
265eb67fb netem: eliminate ... |
859 |
if (tb[TCA_NETEM_CORRUPT]) |
49545a777 sch_netem: change... |
860 |
get_corrupt(q, tb[TCA_NETEM_CORRUPT]); |
1da177e4c Linux-2.6.12-rc2 |
861 |
|
7bc0f28c7 netem: rate exten... |
862 |
if (tb[TCA_NETEM_RATE]) |
49545a777 sch_netem: change... |
863 |
get_rate(q, tb[TCA_NETEM_RATE]); |
7bc0f28c7 netem: rate exten... |
864 |
|
6a031f67c sch_netem: suppor... |
865 866 867 |
if (tb[TCA_NETEM_RATE64]) q->rate = max_t(u64, q->rate, nla_get_u64(tb[TCA_NETEM_RATE64])); |
e4ae004b8 netem: add ECN ca... |
868 869 |
if (tb[TCA_NETEM_ECN]) q->ecn = nla_get_u32(tb[TCA_NETEM_ECN]); |
661b79725 netem: revised co... |
870 |
return ret; |
1da177e4c Linux-2.6.12-rc2 |
871 |
} |
1e90474c3 [NET_SCHED]: Conv... |
872 |
static int netem_init(struct Qdisc *sch, struct nlattr *opt) |
1da177e4c Linux-2.6.12-rc2 |
873 874 875 |
{ struct netem_sched_data *q = qdisc_priv(sch); int ret; |
634576a18 sch_netem: avoid ... |
876 |
qdisc_watchdog_init(&q->watchdog, sch); |
1da177e4c Linux-2.6.12-rc2 |
877 878 |
if (!opt) return -EINVAL; |
661b79725 netem: revised co... |
879 |
q->loss_model = CLG_RANDOM; |
1da177e4c Linux-2.6.12-rc2 |
880 |
ret = netem_change(sch, opt); |
50612537e netem: fix classf... |
881 |
if (ret) |
250a65f78 netem: update ver... |
882 883 |
pr_info("netem: change failed "); |
1da177e4c Linux-2.6.12-rc2 |
884 885 886 887 888 889 |
return ret; } static void netem_destroy(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); |
59cb5c673 [NET_SCHED]: sch_... |
890 |
qdisc_watchdog_cancel(&q->watchdog); |
50612537e netem: fix classf... |
891 892 |
if (q->qdisc) qdisc_destroy(q->qdisc); |
6373a9a28 netem: use vmallo... |
893 |
dist_free(q->delay_dist); |
1da177e4c Linux-2.6.12-rc2 |
894 |
} |
661b79725 netem: revised co... |
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 |
static int dump_loss_model(const struct netem_sched_data *q, struct sk_buff *skb) { struct nlattr *nest; nest = nla_nest_start(skb, TCA_NETEM_LOSS); if (nest == NULL) goto nla_put_failure; switch (q->loss_model) { case CLG_RANDOM: /* legacy loss model */ nla_nest_cancel(skb, nest); return 0; /* no data */ case CLG_4_STATES: { struct tc_netem_gimodel gi = { .p13 = q->clg.a1, .p31 = q->clg.a2, .p32 = q->clg.a3, .p14 = q->clg.a4, .p23 = q->clg.a5, }; |
1b34ec43c pkt_sched: Stop u... |
918 919 |
if (nla_put(skb, NETEM_LOSS_GI, sizeof(gi), &gi)) goto nla_put_failure; |
661b79725 netem: revised co... |
920 921 922 923 924 925 926 927 928 |
break; } case CLG_GILB_ELL: { struct tc_netem_gemodel ge = { .p = q->clg.a1, .r = q->clg.a2, .h = q->clg.a3, .k1 = q->clg.a4, }; |
1b34ec43c pkt_sched: Stop u... |
929 930 |
if (nla_put(skb, NETEM_LOSS_GE, sizeof(ge), &ge)) goto nla_put_failure; |
661b79725 netem: revised co... |
931 932 933 934 935 936 937 938 939 940 941 |
break; } } nla_nest_end(skb, nest); return 0; nla_put_failure: nla_nest_cancel(skb, nest); return -1; } |
1da177e4c Linux-2.6.12-rc2 |
942 943 944 |
static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) { const struct netem_sched_data *q = qdisc_priv(sch); |
861d7f745 netem: cleanup du... |
945 |
struct nlattr *nla = (struct nlattr *) skb_tail_pointer(skb); |
1da177e4c Linux-2.6.12-rc2 |
946 947 |
struct tc_netem_qopt qopt; struct tc_netem_corr cor; |
0dca51d36 [PKT_SCHED] netem... |
948 |
struct tc_netem_reorder reorder; |
c865e5d99 [PKT_SCHED] netem... |
949 |
struct tc_netem_corrupt corrupt; |
7bc0f28c7 netem: rate exten... |
950 |
struct tc_netem_rate rate; |
1da177e4c Linux-2.6.12-rc2 |
951 952 953 954 955 956 957 |
qopt.latency = q->latency; qopt.jitter = q->jitter; qopt.limit = q->limit; qopt.loss = q->loss; qopt.gap = q->gap; qopt.duplicate = q->duplicate; |
1b34ec43c pkt_sched: Stop u... |
958 959 |
if (nla_put(skb, TCA_OPTIONS, sizeof(qopt), &qopt)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
960 961 962 963 |
cor.delay_corr = q->delay_cor.rho; cor.loss_corr = q->loss_cor.rho; cor.dup_corr = q->dup_cor.rho; |
1b34ec43c pkt_sched: Stop u... |
964 965 |
if (nla_put(skb, TCA_NETEM_CORR, sizeof(cor), &cor)) goto nla_put_failure; |
0dca51d36 [PKT_SCHED] netem... |
966 967 968 |
reorder.probability = q->reorder; reorder.correlation = q->reorder_cor.rho; |
1b34ec43c pkt_sched: Stop u... |
969 970 |
if (nla_put(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder)) goto nla_put_failure; |
0dca51d36 [PKT_SCHED] netem... |
971 |
|
c865e5d99 [PKT_SCHED] netem... |
972 973 |
corrupt.probability = q->corrupt; corrupt.correlation = q->corrupt_cor.rho; |
1b34ec43c pkt_sched: Stop u... |
974 975 |
if (nla_put(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt)) goto nla_put_failure; |
c865e5d99 [PKT_SCHED] netem... |
976 |
|
6a031f67c sch_netem: suppor... |
977 |
if (q->rate >= (1ULL << 32)) { |
2a51c1e8e sched: use nla_pu... |
978 979 |
if (nla_put_u64_64bit(skb, TCA_NETEM_RATE64, q->rate, TCA_NETEM_PAD)) |
6a031f67c sch_netem: suppor... |
980 981 982 983 984 |
goto nla_put_failure; rate.rate = ~0U; } else { rate.rate = q->rate; } |
90b41a1cd netem: add cell c... |
985 986 987 |
rate.packet_overhead = q->packet_overhead; rate.cell_size = q->cell_size; rate.cell_overhead = q->cell_overhead; |
1b34ec43c pkt_sched: Stop u... |
988 989 |
if (nla_put(skb, TCA_NETEM_RATE, sizeof(rate), &rate)) goto nla_put_failure; |
7bc0f28c7 netem: rate exten... |
990 |
|
e4ae004b8 netem: add ECN ca... |
991 992 |
if (q->ecn && nla_put_u32(skb, TCA_NETEM_ECN, q->ecn)) goto nla_put_failure; |
661b79725 netem: revised co... |
993 994 |
if (dump_loss_model(q, skb) != 0) goto nla_put_failure; |
861d7f745 netem: cleanup du... |
995 |
return nla_nest_end(skb, nla); |
1da177e4c Linux-2.6.12-rc2 |
996 |
|
1e90474c3 [NET_SCHED]: Conv... |
997 |
nla_put_failure: |
861d7f745 netem: cleanup du... |
998 |
nlmsg_trim(skb, nla); |
1da177e4c Linux-2.6.12-rc2 |
999 1000 |
return -1; } |
10f6dfcfd Revert "sch_netem... |
1001 1002 1003 1004 |
static int netem_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { struct netem_sched_data *q = qdisc_priv(sch); |
50612537e netem: fix classf... |
1005 |
if (cl != 1 || !q->qdisc) /* only one class */ |
10f6dfcfd Revert "sch_netem... |
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 |
return -ENOENT; tcm->tcm_handle |= TC_H_MIN(1); tcm->tcm_info = q->qdisc->handle; return 0; } static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, struct Qdisc **old) { struct netem_sched_data *q = qdisc_priv(sch); |
86a7996cc net_sched: introd... |
1018 |
*old = qdisc_replace(sch, new, &q->qdisc); |
10f6dfcfd Revert "sch_netem... |
1019 1020 1021 1022 1023 1024 1025 1026 |
return 0; } static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg) { struct netem_sched_data *q = qdisc_priv(sch); return q->qdisc; } |
143976ce9 net_sched: remove... |
1027 |
static unsigned long netem_find(struct Qdisc *sch, u32 classid) |
10f6dfcfd Revert "sch_netem... |
1028 1029 1030 |
{ return 1; } |
10f6dfcfd Revert "sch_netem... |
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 |
static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker) { if (!walker->stop) { if (walker->count >= walker->skip) if (walker->fn(sch, 1, walker) < 0) { walker->stop = 1; return; } walker->count++; } } static const struct Qdisc_class_ops netem_class_ops = { .graft = netem_graft, .leaf = netem_leaf, |
143976ce9 net_sched: remove... |
1046 |
.find = netem_find, |
10f6dfcfd Revert "sch_netem... |
1047 1048 1049 |
.walk = netem_walk, .dump = netem_dump_class, }; |
20fea08b5 [NET]: Move Qdisc... |
1050 |
static struct Qdisc_ops netem_qdisc_ops __read_mostly = { |
1da177e4c Linux-2.6.12-rc2 |
1051 |
.id = "netem", |
10f6dfcfd Revert "sch_netem... |
1052 |
.cl_ops = &netem_class_ops, |
1da177e4c Linux-2.6.12-rc2 |
1053 1054 1055 |
.priv_size = sizeof(struct netem_sched_data), .enqueue = netem_enqueue, .dequeue = netem_dequeue, |
77be155cb pkt_sched: Add pe... |
1056 |
.peek = qdisc_peek_dequeued, |
1da177e4c Linux-2.6.12-rc2 |
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 |
.init = netem_init, .reset = netem_reset, .destroy = netem_destroy, .change = netem_change, .dump = netem_dump, .owner = THIS_MODULE, }; static int __init netem_module_init(void) { |
eb229c4cd [NETEM]: Add vers... |
1068 1069 |
pr_info("netem: version " VERSION " "); |
1da177e4c Linux-2.6.12-rc2 |
1070 1071 1072 1073 1074 1075 1076 1077 1078 |
return register_qdisc(&netem_qdisc_ops); } static void __exit netem_module_exit(void) { unregister_qdisc(&netem_qdisc_ops); } module_init(netem_module_init) module_exit(netem_module_exit) MODULE_LICENSE("GPL"); |