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.c122
1 files changed, 109 insertions, 13 deletions
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index bb9bf8d5003c..cdc8d283791c 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -25,6 +25,8 @@
25 25
26#include <net/pkt_sched.h> 26#include <net/pkt_sched.h>
27 27
28#define VERSION "1.1"
29
28/* Network Emulation Queuing algorithm. 30/* Network Emulation Queuing algorithm.
29 ==================================== 31 ====================================
30 32
@@ -185,10 +187,13 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
185 || q->counter < q->gap /* inside last reordering gap */ 187 || q->counter < q->gap /* inside last reordering gap */
186 || q->reorder < get_crandom(&q->reorder_cor)) { 188 || q->reorder < get_crandom(&q->reorder_cor)) {
187 psched_time_t now; 189 psched_time_t now;
190 psched_tdiff_t delay;
191
192 delay = tabledist(q->latency, q->jitter,
193 &q->delay_cor, q->delay_dist);
194
188 PSCHED_GET_TIME(now); 195 PSCHED_GET_TIME(now);
189 PSCHED_TADD2(now, tabledist(q->latency, q->jitter, 196 PSCHED_TADD2(now, delay, cb->time_to_send);
190 &q->delay_cor, q->delay_dist),
191 cb->time_to_send);
192 ++q->counter; 197 ++q->counter;
193 ret = q->qdisc->enqueue(skb, q->qdisc); 198 ret = q->qdisc->enqueue(skb, q->qdisc);
194 } else { 199 } else {
@@ -248,24 +253,31 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
248 const struct netem_skb_cb *cb 253 const struct netem_skb_cb *cb
249 = (const struct netem_skb_cb *)skb->cb; 254 = (const struct netem_skb_cb *)skb->cb;
250 psched_time_t now; 255 psched_time_t now;
251 long delay;
252 256
253 /* if more time remaining? */ 257 /* if more time remaining? */
254 PSCHED_GET_TIME(now); 258 PSCHED_GET_TIME(now);
255 delay = PSCHED_US2JIFFIE(PSCHED_TDIFF(cb->time_to_send, now)); 259
256 pr_debug("netem_run: skb=%p delay=%ld\n", skb, delay); 260 if (PSCHED_TLESS(cb->time_to_send, now)) {
257 if (delay <= 0) {
258 pr_debug("netem_dequeue: return skb=%p\n", skb); 261 pr_debug("netem_dequeue: return skb=%p\n", skb);
259 sch->q.qlen--; 262 sch->q.qlen--;
260 sch->flags &= ~TCQ_F_THROTTLED; 263 sch->flags &= ~TCQ_F_THROTTLED;
261 return skb; 264 return skb;
262 } 265 } else {
266 psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now);
267
268 if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
269 sch->qstats.drops++;
263 270
264 mod_timer(&q->timer, jiffies + delay); 271 /* After this qlen is confused */
265 sch->flags |= TCQ_F_THROTTLED; 272 printk(KERN_ERR "netem: queue discpline %s could not requeue\n",
273 q->qdisc->ops->id);
266 274
267 if (q->qdisc->ops->requeue(skb, q->qdisc) != 0) 275 sch->q.qlen--;
268 sch->qstats.drops++; 276 }
277
278 mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay));
279 sch->flags |= TCQ_F_THROTTLED;
280 }
269 } 281 }
270 282
271 return NULL; 283 return NULL;
@@ -290,11 +302,16 @@ static void netem_reset(struct Qdisc *sch)
290 del_timer_sync(&q->timer); 302 del_timer_sync(&q->timer);
291} 303}
292 304
305/* Pass size change message down to embedded FIFO */
293static int set_fifo_limit(struct Qdisc *q, int limit) 306static int set_fifo_limit(struct Qdisc *q, int limit)
294{ 307{
295 struct rtattr *rta; 308 struct rtattr *rta;
296 int ret = -ENOMEM; 309 int ret = -ENOMEM;
297 310
311 /* Hack to avoid sending change message to non-FIFO */
312 if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
313 return 0;
314
298 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); 315 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
299 if (rta) { 316 if (rta) {
300 rta->rta_type = RTM_NEWQDISC; 317 rta->rta_type = RTM_NEWQDISC;
@@ -426,6 +443,84 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
426 return 0; 443 return 0;
427} 444}
428 445
446/*
447 * Special case version of FIFO queue for use by netem.
448 * It queues in order based on timestamps in skb's
449 */
450struct fifo_sched_data {
451 u32 limit;
452};
453
454static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
455{
456 struct fifo_sched_data *q = qdisc_priv(sch);
457 struct sk_buff_head *list = &sch->q;
458 const struct netem_skb_cb *ncb
459 = (const struct netem_skb_cb *)nskb->cb;
460 struct sk_buff *skb;
461
462 if (likely(skb_queue_len(list) < q->limit)) {
463 skb_queue_reverse_walk(list, skb) {
464 const struct netem_skb_cb *cb
465 = (const struct netem_skb_cb *)skb->cb;
466
467 if (PSCHED_TLESS(cb->time_to_send, ncb->time_to_send))
468 break;
469 }
470
471 __skb_queue_after(list, skb, nskb);
472
473 sch->qstats.backlog += nskb->len;
474 sch->bstats.bytes += nskb->len;
475 sch->bstats.packets++;
476
477 return NET_XMIT_SUCCESS;
478 }
479
480 return qdisc_drop(nskb, sch);
481}
482
483static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
484{
485 struct fifo_sched_data *q = qdisc_priv(sch);
486
487 if (opt) {
488 struct tc_fifo_qopt *ctl = RTA_DATA(opt);
489 if (RTA_PAYLOAD(opt) < sizeof(*ctl))
490 return -EINVAL;
491
492 q->limit = ctl->limit;
493 } else
494 q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
495
496 return 0;
497}
498
499static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb)
500{
501 struct fifo_sched_data *q = qdisc_priv(sch);
502 struct tc_fifo_qopt opt = { .limit = q->limit };
503
504 RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
505 return skb->len;
506
507rtattr_failure:
508 return -1;
509}
510
511static struct Qdisc_ops tfifo_qdisc_ops = {
512 .id = "tfifo",
513 .priv_size = sizeof(struct fifo_sched_data),
514 .enqueue = tfifo_enqueue,
515 .dequeue = qdisc_dequeue_head,
516 .requeue = qdisc_requeue,
517 .drop = qdisc_queue_drop,
518 .init = tfifo_init,
519 .reset = qdisc_reset_queue,
520 .change = tfifo_init,
521 .dump = tfifo_dump,
522};
523
429static int netem_init(struct Qdisc *sch, struct rtattr *opt) 524static int netem_init(struct Qdisc *sch, struct rtattr *opt)
430{ 525{
431 struct netem_sched_data *q = qdisc_priv(sch); 526 struct netem_sched_data *q = qdisc_priv(sch);
@@ -438,7 +533,7 @@ static int netem_init(struct Qdisc *sch, struct rtattr *opt)
438 q->timer.function = netem_watchdog; 533 q->timer.function = netem_watchdog;
439 q->timer.data = (unsigned long) sch; 534 q->timer.data = (unsigned long) sch;
440 535
441 q->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); 536 q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops);
442 if (!q->qdisc) { 537 if (!q->qdisc) {
443 pr_debug("netem: qdisc create failed\n"); 538 pr_debug("netem: qdisc create failed\n");
444 return -ENOMEM; 539 return -ENOMEM;
@@ -601,6 +696,7 @@ static struct Qdisc_ops netem_qdisc_ops = {
601 696
602static int __init netem_module_init(void) 697static int __init netem_module_init(void)
603{ 698{
699 pr_info("netem: version " VERSION "\n");
604 return register_qdisc(&netem_qdisc_ops); 700 return register_qdisc(&netem_qdisc_ops);
605} 701}
606static void __exit netem_module_exit(void) 702static void __exit netem_module_exit(void)