aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched/sch_netem.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched/sch_netem.c')
-rw-r--r--net/sched/sch_netem.c108
1 files changed, 53 insertions, 55 deletions
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 1ccbfb55b0b8..5d9d8bc9cc3a 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -22,6 +22,7 @@
22#include <linux/skbuff.h> 22#include <linux/skbuff.h>
23#include <linux/rtnetlink.h> 23#include <linux/rtnetlink.h>
24 24
25#include <net/netlink.h>
25#include <net/pkt_sched.h> 26#include <net/pkt_sched.h>
26 27
27#define VERSION "1.2" 28#define VERSION "1.2"
@@ -54,21 +55,22 @@
54 55
55struct netem_sched_data { 56struct netem_sched_data {
56 struct Qdisc *qdisc; 57 struct Qdisc *qdisc;
57 struct timer_list timer; 58 struct qdisc_watchdog watchdog;
59
60 psched_tdiff_t latency;
61 psched_tdiff_t jitter;
58 62
59 u32 latency;
60 u32 loss; 63 u32 loss;
61 u32 limit; 64 u32 limit;
62 u32 counter; 65 u32 counter;
63 u32 gap; 66 u32 gap;
64 u32 jitter;
65 u32 duplicate; 67 u32 duplicate;
66 u32 reorder; 68 u32 reorder;
67 u32 corrupt; 69 u32 corrupt;
68 70
69 struct crndstate { 71 struct crndstate {
70 unsigned long last; 72 u32 last;
71 unsigned long rho; 73 u32 rho;
72 } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor; 74 } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
73 75
74 struct disttable { 76 struct disttable {
@@ -95,12 +97,12 @@ static void init_crandom(struct crndstate *state, unsigned long rho)
95 * Next number depends on last value. 97 * Next number depends on last value.
96 * rho is scaled to avoid floating point. 98 * rho is scaled to avoid floating point.
97 */ 99 */
98static unsigned long get_crandom(struct crndstate *state) 100static u32 get_crandom(struct crndstate *state)
99{ 101{
100 u64 value, rho; 102 u64 value, rho;
101 unsigned long answer; 103 unsigned long answer;
102 104
103 if (state->rho == 0) /* no correllation */ 105 if (state->rho == 0) /* no correlation */
104 return net_random(); 106 return net_random();
105 107
106 value = net_random(); 108 value = net_random();
@@ -114,11 +116,13 @@ static unsigned long get_crandom(struct crndstate *state)
114 * std deviation sigma. Uses table lookup to approximate the desired 116 * std deviation sigma. Uses table lookup to approximate the desired
115 * distribution, and a uniformly-distributed pseudo-random source. 117 * distribution, and a uniformly-distributed pseudo-random source.
116 */ 118 */
117static long tabledist(unsigned long mu, long sigma, 119static psched_tdiff_t tabledist(psched_tdiff_t mu, psched_tdiff_t sigma,
118 struct crndstate *state, const struct disttable *dist) 120 struct crndstate *state,
121 const struct disttable *dist)
119{ 122{
120 long t, x; 123 psched_tdiff_t x;
121 unsigned long rnd; 124 long t;
125 u32 rnd;
122 126
123 if (sigma == 0) 127 if (sigma == 0)
124 return mu; 128 return mu;
@@ -213,8 +217,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
213 delay = tabledist(q->latency, q->jitter, 217 delay = tabledist(q->latency, q->jitter,
214 &q->delay_cor, q->delay_dist); 218 &q->delay_cor, q->delay_dist);
215 219
216 PSCHED_GET_TIME(now); 220 now = psched_get_time();
217 PSCHED_TADD2(now, delay, cb->time_to_send); 221 cb->time_to_send = now + delay;
218 ++q->counter; 222 ++q->counter;
219 ret = q->qdisc->enqueue(skb, q->qdisc); 223 ret = q->qdisc->enqueue(skb, q->qdisc);
220 } else { 224 } else {
@@ -222,7 +226,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
222 * Do re-ordering by putting one out of N packets at the front 226 * Do re-ordering by putting one out of N packets at the front
223 * of the queue. 227 * of the queue.
224 */ 228 */
225 PSCHED_GET_TIME(cb->time_to_send); 229 cb->time_to_send = psched_get_time();
226 q->counter = 0; 230 q->counter = 0;
227 ret = q->qdisc->ops->requeue(skb, q->qdisc); 231 ret = q->qdisc->ops->requeue(skb, q->qdisc);
228 } 232 }
@@ -269,55 +273,43 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
269 struct netem_sched_data *q = qdisc_priv(sch); 273 struct netem_sched_data *q = qdisc_priv(sch);
270 struct sk_buff *skb; 274 struct sk_buff *skb;
271 275
276 smp_mb();
277 if (sch->flags & TCQ_F_THROTTLED)
278 return NULL;
279
272 skb = q->qdisc->dequeue(q->qdisc); 280 skb = q->qdisc->dequeue(q->qdisc);
273 if (skb) { 281 if (skb) {
274 const struct netem_skb_cb *cb 282 const struct netem_skb_cb *cb
275 = (const struct netem_skb_cb *)skb->cb; 283 = (const struct netem_skb_cb *)skb->cb;
276 psched_time_t now; 284 psched_time_t now = psched_get_time();
277 285
278 /* if more time remaining? */ 286 /* if more time remaining? */
279 PSCHED_GET_TIME(now); 287 if (cb->time_to_send <= now) {
280
281 if (PSCHED_TLESS(cb->time_to_send, now)) {
282 pr_debug("netem_dequeue: return skb=%p\n", skb); 288 pr_debug("netem_dequeue: return skb=%p\n", skb);
283 sch->q.qlen--; 289 sch->q.qlen--;
284 sch->flags &= ~TCQ_F_THROTTLED;
285 return skb; 290 return skb;
286 } else { 291 }
287 psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now);
288
289 if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
290 qdisc_tree_decrease_qlen(q->qdisc, 1);
291 sch->qstats.drops++;
292 printk(KERN_ERR "netem: queue discpline %s could not requeue\n",
293 q->qdisc->ops->id);
294 }
295 292
296 mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay)); 293 if (unlikely(q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS)) {
297 sch->flags |= TCQ_F_THROTTLED; 294 qdisc_tree_decrease_qlen(q->qdisc, 1);
295 sch->qstats.drops++;
296 printk(KERN_ERR "netem: %s could not requeue\n",
297 q->qdisc->ops->id);
298 } 298 }
299
300 qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send);
299 } 301 }
300 302
301 return NULL; 303 return NULL;
302} 304}
303 305
304static void netem_watchdog(unsigned long arg)
305{
306 struct Qdisc *sch = (struct Qdisc *)arg;
307
308 pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen);
309 sch->flags &= ~TCQ_F_THROTTLED;
310 netif_schedule(sch->dev);
311}
312
313static void netem_reset(struct Qdisc *sch) 306static void netem_reset(struct Qdisc *sch)
314{ 307{
315 struct netem_sched_data *q = qdisc_priv(sch); 308 struct netem_sched_data *q = qdisc_priv(sch);
316 309
317 qdisc_reset(q->qdisc); 310 qdisc_reset(q->qdisc);
318 sch->q.qlen = 0; 311 sch->q.qlen = 0;
319 sch->flags &= ~TCQ_F_THROTTLED; 312 qdisc_watchdog_cancel(&q->watchdog);
320 del_timer_sync(&q->timer);
321} 313}
322 314
323/* Pass size change message down to embedded FIFO */ 315/* Pass size change message down to embedded FIFO */
@@ -438,10 +430,11 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
438 q->loss = qopt->loss; 430 q->loss = qopt->loss;
439 q->duplicate = qopt->duplicate; 431 q->duplicate = qopt->duplicate;
440 432
441 /* for compatiablity with earlier versions. 433 /* for compatibility with earlier versions.
442 * if gap is set, need to assume 100% probablity 434 * if gap is set, need to assume 100% probability
443 */ 435 */
444 q->reorder = ~0; 436 if (q->gap)
437 q->reorder = ~0;
445 438
446 /* Handle nested options after initial queue options. 439 /* Handle nested options after initial queue options.
447 * Should have put all options in nested format but too late now. 440 * Should have put all options in nested format but too late now.
@@ -487,22 +480,28 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
487 */ 480 */
488struct fifo_sched_data { 481struct fifo_sched_data {
489 u32 limit; 482 u32 limit;
483 psched_time_t oldest;
490}; 484};
491 485
492static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) 486static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
493{ 487{
494 struct fifo_sched_data *q = qdisc_priv(sch); 488 struct fifo_sched_data *q = qdisc_priv(sch);
495 struct sk_buff_head *list = &sch->q; 489 struct sk_buff_head *list = &sch->q;
496 const struct netem_skb_cb *ncb 490 psched_time_t tnext = ((struct netem_skb_cb *)nskb->cb)->time_to_send;
497 = (const struct netem_skb_cb *)nskb->cb;
498 struct sk_buff *skb; 491 struct sk_buff *skb;
499 492
500 if (likely(skb_queue_len(list) < q->limit)) { 493 if (likely(skb_queue_len(list) < q->limit)) {
494 /* Optimize for add at tail */
495 if (likely(skb_queue_empty(list) || tnext >= q->oldest)) {
496 q->oldest = tnext;
497 return qdisc_enqueue_tail(nskb, sch);
498 }
499
501 skb_queue_reverse_walk(list, skb) { 500 skb_queue_reverse_walk(list, skb) {
502 const struct netem_skb_cb *cb 501 const struct netem_skb_cb *cb
503 = (const struct netem_skb_cb *)skb->cb; 502 = (const struct netem_skb_cb *)skb->cb;
504 503
505 if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send)) 504 if (tnext >= cb->time_to_send)
506 break; 505 break;
507 } 506 }
508 507
@@ -515,7 +514,7 @@ static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
515 return NET_XMIT_SUCCESS; 514 return NET_XMIT_SUCCESS;
516 } 515 }
517 516
518 return qdisc_drop(nskb, sch); 517 return qdisc_reshape_fail(nskb, sch);
519} 518}
520 519
521static int tfifo_init(struct Qdisc *sch, struct rtattr *opt) 520static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
@@ -531,6 +530,7 @@ static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
531 } else 530 } else
532 q->limit = max_t(u32, sch->dev->tx_queue_len, 1); 531 q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
533 532
533 q->oldest = PSCHED_PASTPERFECT;
534 return 0; 534 return 0;
535} 535}
536 536
@@ -567,9 +567,7 @@ static int netem_init(struct Qdisc *sch, struct rtattr *opt)
567 if (!opt) 567 if (!opt)
568 return -EINVAL; 568 return -EINVAL;
569 569
570 init_timer(&q->timer); 570 qdisc_watchdog_init(&q->watchdog, sch);
571 q->timer.function = netem_watchdog;
572 q->timer.data = (unsigned long) sch;
573 571
574 q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops, 572 q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops,
575 TC_H_MAKE(sch->handle, 1)); 573 TC_H_MAKE(sch->handle, 1));
@@ -590,7 +588,7 @@ static void netem_destroy(struct Qdisc *sch)
590{ 588{
591 struct netem_sched_data *q = qdisc_priv(sch); 589 struct netem_sched_data *q = qdisc_priv(sch);
592 590
593 del_timer_sync(&q->timer); 591 qdisc_watchdog_cancel(&q->watchdog);
594 qdisc_destroy(q->qdisc); 592 qdisc_destroy(q->qdisc);
595 kfree(q->delay_dist); 593 kfree(q->delay_dist);
596} 594}
@@ -598,7 +596,7 @@ static void netem_destroy(struct Qdisc *sch)
598static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) 596static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
599{ 597{
600 const struct netem_sched_data *q = qdisc_priv(sch); 598 const struct netem_sched_data *q = qdisc_priv(sch);
601 unsigned char *b = skb->tail; 599 unsigned char *b = skb_tail_pointer(skb);
602 struct rtattr *rta = (struct rtattr *) b; 600 struct rtattr *rta = (struct rtattr *) b;
603 struct tc_netem_qopt qopt; 601 struct tc_netem_qopt qopt;
604 struct tc_netem_corr cor; 602 struct tc_netem_corr cor;
@@ -626,12 +624,12 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
626 corrupt.correlation = q->corrupt_cor.rho; 624 corrupt.correlation = q->corrupt_cor.rho;
627 RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); 625 RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
628 626
629 rta->rta_len = skb->tail - b; 627 rta->rta_len = skb_tail_pointer(skb) - b;
630 628
631 return skb->len; 629 return skb->len;
632 630
633rtattr_failure: 631rtattr_failure:
634 skb_trim(skb, b - skb->data); 632 nlmsg_trim(skb, b);
635 return -1; 633 return -1;
636} 634}
637 635