aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2011-02-23 08:04:21 -0500
committerDavid S. Miller <davem@davemloft.net>2011-02-25 01:11:56 -0500
commit661b79725fea030803a89a16cda506bac8eeca78 (patch)
treeb13a294ecb7696c2c96db88f5b98627f5c4d4ad5 /net/sched
parent10f6dfcfde884441db89dc66b945d6c948e1d356 (diff)
netem: revised correlated loss generator
This is a patch originated with Stefano Salsano and Fabio Ludovici. It provides several alternative loss models for use with netem. This patch adds two state machine based loss models. See: http://netgroup.uniroma2.it/twiki/bin/view.cgi/Main/NetemCLG Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched')
-rw-r--r--net/sched/sch_netem.c274
1 files changed, 270 insertions, 4 deletions
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index f176890eeef0..5bbcccc353d0 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -47,6 +47,20 @@
47 layering other disciplines. It does not need to do bandwidth 47 layering other disciplines. It does not need to do bandwidth
48 control either since that can be handled by using token 48 control either since that can be handled by using token
49 bucket or other rate control. 49 bucket or other rate control.
50
51 Correlated Loss Generator models
52
53 Added generation of correlated loss according to the
54 "Gilbert-Elliot" model, a 4-state markov model.
55
56 References:
57 [1] NetemCLG Home http://netgroup.uniroma2.it/NetemCLG
58 [2] S. Salsano, F. Ludovici, A. Ordine, "Definition of a general
59 and intuitive loss model for packet networks and its implementation
60 in the Netem module in the Linux kernel", available in [1]
61
62 Authors: Stefano Salsano <stefano.salsano at uniroma2.it
63 Fabio Ludovici <fabio.ludovici at yahoo.it>
50*/ 64*/
51 65
52struct netem_sched_data { 66struct netem_sched_data {
@@ -73,6 +87,26 @@ struct netem_sched_data {
73 u32 size; 87 u32 size;
74 s16 table[0]; 88 s16 table[0];
75 } *delay_dist; 89 } *delay_dist;
90
91 enum {
92 CLG_RANDOM,
93 CLG_4_STATES,
94 CLG_GILB_ELL,
95 } loss_model;
96
97 /* Correlated Loss Generation models */
98 struct clgstate {
99 /* state of the Markov chain */
100 u8 state;
101
102 /* 4-states and Gilbert-Elliot models */
103 u32 a1; /* p13 for 4-states or p for GE */
104 u32 a2; /* p31 for 4-states or r for GE */
105 u32 a3; /* p32 for 4-states or h for GE */
106 u32 a4; /* p14 for 4-states or 1-k for GE */
107 u32 a5; /* p23 used only in 4-states */
108 } clg;
109
76}; 110};
77 111
78/* Time stamp put into socket buffer control block */ 112/* Time stamp put into socket buffer control block */
@@ -115,6 +149,122 @@ static u32 get_crandom(struct crndstate *state)
115 return answer; 149 return answer;
116} 150}
117 151
152/* loss_4state - 4-state model loss generator
153 * Generates losses according to the 4-state Markov chain adopted in
154 * the GI (General and Intuitive) loss model.
155 */
156static bool loss_4state(struct netem_sched_data *q)
157{
158 struct clgstate *clg = &q->clg;
159 u32 rnd = net_random();
160
161 /*
162 * Makes a comparision between rnd and the transition
163 * probabilities outgoing from the current state, then decides the
164 * next state and if the next packet has to be transmitted or lost.
165 * The four states correspond to:
166 * 1 => successfully transmitted packets within a gap period
167 * 4 => isolated losses within a gap period
168 * 3 => lost packets within a burst period
169 * 2 => successfully transmitted packets within a burst period
170 */
171 switch (clg->state) {
172 case 1:
173 if (rnd < clg->a4) {
174 clg->state = 4;
175 return true;
176 } else if (clg->a4 < rnd && rnd < clg->a1) {
177 clg->state = 3;
178 return true;
179 } else if (clg->a1 < rnd)
180 clg->state = 1;
181
182 break;
183 case 2:
184 if (rnd < clg->a5) {
185 clg->state = 3;
186 return true;
187 } else
188 clg->state = 2;
189
190 break;
191 case 3:
192 if (rnd < clg->a3)
193 clg->state = 2;
194 else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) {
195 clg->state = 1;
196 return true;
197 } else if (clg->a2 + clg->a3 < rnd) {
198 clg->state = 3;
199 return true;
200 }
201 break;
202 case 4:
203 clg->state = 1;
204 break;
205 }
206
207 return false;
208}
209
210/* loss_gilb_ell - Gilbert-Elliot model loss generator
211 * Generates losses according to the Gilbert-Elliot loss model or
212 * its special cases (Gilbert or Simple Gilbert)
213 *
214 * Makes a comparision between random number and the transition
215 * probabilities outgoing from the current state, then decides the
216 * next state. A second random number is extracted and the comparision
217 * with the loss probability of the current state decides if the next
218 * packet will be transmitted or lost.
219 */
220static bool loss_gilb_ell(struct netem_sched_data *q)
221{
222 struct clgstate *clg = &q->clg;
223
224 switch (clg->state) {
225 case 1:
226 if (net_random() < clg->a1)
227 clg->state = 2;
228 if (net_random() < clg->a4)
229 return true;
230 case 2:
231 if (net_random() < clg->a2)
232 clg->state = 1;
233 if (clg->a3 > net_random())
234 return true;
235 }
236
237 return false;
238}
239
240static bool loss_event(struct netem_sched_data *q)
241{
242 switch (q->loss_model) {
243 case CLG_RANDOM:
244 /* Random packet drop 0 => none, ~0 => all */
245 return q->loss && q->loss >= get_crandom(&q->loss_cor);
246
247 case CLG_4_STATES:
248 /* 4state loss model algorithm (used also for GI model)
249 * Extracts a value from the markov 4 state loss generator,
250 * if it is 1 drops a packet and if needed writes the event in
251 * the kernel logs
252 */
253 return loss_4state(q);
254
255 case CLG_GILB_ELL:
256 /* Gilbert-Elliot loss model algorithm
257 * Extracts a value from the Gilbert-Elliot loss generator,
258 * if it is 1 drops a packet and if needed writes the event in
259 * the kernel logs
260 */
261 return loss_gilb_ell(q);
262 }
263
264 return false; /* not reached */
265}
266
267
118/* tabledist - return a pseudo-randomly distributed value with mean mu and 268/* tabledist - return a pseudo-randomly distributed value with mean mu and
119 * std deviation sigma. Uses table lookup to approximate the desired 269 * std deviation sigma. Uses table lookup to approximate the desired
120 * distribution, and a uniformly-distributed pseudo-random source. 270 * distribution, and a uniformly-distributed pseudo-random source.
@@ -167,8 +317,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
167 if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)) 317 if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
168 ++count; 318 ++count;
169 319
170 /* Random packet drop 0 => none, ~0 => all */ 320 /* Drop packet? */
171 if (q->loss && q->loss >= get_crandom(&q->loss_cor)) 321 if (loss_event(q))
172 --count; 322 --count;
173 323
174 if (count == 0) { 324 if (count == 0) {
@@ -385,10 +535,66 @@ static void get_corrupt(struct Qdisc *sch, const struct nlattr *attr)
385 init_crandom(&q->corrupt_cor, r->correlation); 535 init_crandom(&q->corrupt_cor, r->correlation);
386} 536}
387 537
538static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr)
539{
540 struct netem_sched_data *q = qdisc_priv(sch);
541 const struct nlattr *la;
542 int rem;
543
544 nla_for_each_nested(la, attr, rem) {
545 u16 type = nla_type(la);
546
547 switch(type) {
548 case NETEM_LOSS_GI: {
549 const struct tc_netem_gimodel *gi = nla_data(la);
550
551 if (nla_len(la) != sizeof(struct tc_netem_gimodel)) {
552 pr_info("netem: incorrect gi model size\n");
553 return -EINVAL;
554 }
555
556 q->loss_model = CLG_4_STATES;
557
558 q->clg.state = 1;
559 q->clg.a1 = gi->p13;
560 q->clg.a2 = gi->p31;
561 q->clg.a3 = gi->p32;
562 q->clg.a4 = gi->p14;
563 q->clg.a5 = gi->p23;
564 break;
565 }
566
567 case NETEM_LOSS_GE: {
568 const struct tc_netem_gemodel *ge = nla_data(la);
569
570 if (nla_len(la) != sizeof(struct tc_netem_gemodel)) {
571 pr_info("netem: incorrect gi model size\n");
572 return -EINVAL;
573 }
574
575 q->loss_model = CLG_GILB_ELL;
576 q->clg.state = 1;
577 q->clg.a1 = ge->p;
578 q->clg.a2 = ge->r;
579 q->clg.a3 = ge->h;
580 q->clg.a4 = ge->k1;
581 break;
582 }
583
584 default:
585 pr_info("netem: unknown loss type %u\n", type);
586 return -EINVAL;
587 }
588 }
589
590 return 0;
591}
592
388static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = { 593static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = {
389 [TCA_NETEM_CORR] = { .len = sizeof(struct tc_netem_corr) }, 594 [TCA_NETEM_CORR] = { .len = sizeof(struct tc_netem_corr) },
390 [TCA_NETEM_REORDER] = { .len = sizeof(struct tc_netem_reorder) }, 595 [TCA_NETEM_REORDER] = { .len = sizeof(struct tc_netem_reorder) },
391 [TCA_NETEM_CORRUPT] = { .len = sizeof(struct tc_netem_corrupt) }, 596 [TCA_NETEM_CORRUPT] = { .len = sizeof(struct tc_netem_corrupt) },
597 [TCA_NETEM_LOSS] = { .type = NLA_NESTED },
392}; 598};
393 599
394static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, 600static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
@@ -396,11 +602,15 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
396{ 602{
397 int nested_len = nla_len(nla) - NLA_ALIGN(len); 603 int nested_len = nla_len(nla) - NLA_ALIGN(len);
398 604
399 if (nested_len < 0) 605 if (nested_len < 0) {
606 pr_info("netem: invalid attributes len %d\n", nested_len);
400 return -EINVAL; 607 return -EINVAL;
608 }
609
401 if (nested_len >= nla_attr_size(0)) 610 if (nested_len >= nla_attr_size(0))
402 return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), 611 return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len),
403 nested_len, policy); 612 nested_len, policy);
613
404 memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); 614 memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
405 return 0; 615 return 0;
406} 616}
@@ -456,7 +666,11 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
456 if (tb[TCA_NETEM_CORRUPT]) 666 if (tb[TCA_NETEM_CORRUPT])
457 get_corrupt(sch, tb[TCA_NETEM_CORRUPT]); 667 get_corrupt(sch, tb[TCA_NETEM_CORRUPT]);
458 668
459 return 0; 669 q->loss_model = CLG_RANDOM;
670 if (tb[TCA_NETEM_LOSS])
671 ret = get_loss_clg(sch, tb[TCA_NETEM_LOSS]);
672
673 return ret;
460} 674}
461 675
462/* 676/*
@@ -551,6 +765,7 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt)
551 765
552 qdisc_watchdog_init(&q->watchdog, sch); 766 qdisc_watchdog_init(&q->watchdog, sch);
553 767
768 q->loss_model = CLG_RANDOM;
554 q->qdisc = qdisc_create_dflt(sch->dev_queue, &tfifo_qdisc_ops, 769 q->qdisc = qdisc_create_dflt(sch->dev_queue, &tfifo_qdisc_ops,
555 TC_H_MAKE(sch->handle, 1)); 770 TC_H_MAKE(sch->handle, 1));
556 if (!q->qdisc) { 771 if (!q->qdisc) {
@@ -575,6 +790,54 @@ static void netem_destroy(struct Qdisc *sch)
575 dist_free(q->delay_dist); 790 dist_free(q->delay_dist);
576} 791}
577 792
793static int dump_loss_model(const struct netem_sched_data *q,
794 struct sk_buff *skb)
795{
796 struct nlattr *nest;
797
798 nest = nla_nest_start(skb, TCA_NETEM_LOSS);
799 if (nest == NULL)
800 goto nla_put_failure;
801
802 switch (q->loss_model) {
803 case CLG_RANDOM:
804 /* legacy loss model */
805 nla_nest_cancel(skb, nest);
806 return 0; /* no data */
807
808 case CLG_4_STATES: {
809 struct tc_netem_gimodel gi = {
810 .p13 = q->clg.a1,
811 .p31 = q->clg.a2,
812 .p32 = q->clg.a3,
813 .p14 = q->clg.a4,
814 .p23 = q->clg.a5,
815 };
816
817 NLA_PUT(skb, NETEM_LOSS_GI, sizeof(gi), &gi);
818 break;
819 }
820 case CLG_GILB_ELL: {
821 struct tc_netem_gemodel ge = {
822 .p = q->clg.a1,
823 .r = q->clg.a2,
824 .h = q->clg.a3,
825 .k1 = q->clg.a4,
826 };
827
828 NLA_PUT(skb, NETEM_LOSS_GE, sizeof(ge), &ge);
829 break;
830 }
831 }
832
833 nla_nest_end(skb, nest);
834 return 0;
835
836nla_put_failure:
837 nla_nest_cancel(skb, nest);
838 return -1;
839}
840
578static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) 841static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
579{ 842{
580 const struct netem_sched_data *q = qdisc_priv(sch); 843 const struct netem_sched_data *q = qdisc_priv(sch);
@@ -605,6 +868,9 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
605 corrupt.correlation = q->corrupt_cor.rho; 868 corrupt.correlation = q->corrupt_cor.rho;
606 NLA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); 869 NLA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
607 870
871 if (dump_loss_model(q, skb) != 0)
872 goto nla_put_failure;
873
608 return nla_nest_end(skb, nla); 874 return nla_nest_end(skb, nla);
609 875
610nla_put_failure: 876nla_put_failure: