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.c85
1 files changed, 84 insertions, 1 deletions
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index d871fe7f81a9..7c10ef3457d7 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -300,11 +300,16 @@ static void netem_reset(struct Qdisc *sch)
300 del_timer_sync(&q->timer); 300 del_timer_sync(&q->timer);
301} 301}
302 302
303/* Pass size change message down to embedded FIFO */
303static int set_fifo_limit(struct Qdisc *q, int limit) 304static int set_fifo_limit(struct Qdisc *q, int limit)
304{ 305{
305 struct rtattr *rta; 306 struct rtattr *rta;
306 int ret = -ENOMEM; 307 int ret = -ENOMEM;
307 308
309 /* Hack to avoid sending change message to non-FIFO */
310 if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
311 return 0;
312
308 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); 313 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
309 if (rta) { 314 if (rta) {
310 rta->rta_type = RTM_NEWQDISC; 315 rta->rta_type = RTM_NEWQDISC;
@@ -436,6 +441,84 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
436 return 0; 441 return 0;
437} 442}
438 443
444/*
445 * Special case version of FIFO queue for use by netem.
446 * It queues in order based on timestamps in skb's
447 */
448struct fifo_sched_data {
449 u32 limit;
450};
451
452static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
453{
454 struct fifo_sched_data *q = qdisc_priv(sch);
455 struct sk_buff_head *list = &sch->q;
456 const struct netem_skb_cb *ncb
457 = (const struct netem_skb_cb *)nskb->cb;
458 struct sk_buff *skb;
459
460 if (likely(skb_queue_len(list) < q->limit)) {
461 skb_queue_reverse_walk(list, skb) {
462 const struct netem_skb_cb *cb
463 = (const struct netem_skb_cb *)skb->cb;
464
465 if (PSCHED_TLESS(cb->time_to_send, ncb->time_to_send))
466 break;
467 }
468
469 __skb_queue_after(list, skb, nskb);
470
471 sch->qstats.backlog += nskb->len;
472 sch->bstats.bytes += nskb->len;
473 sch->bstats.packets++;
474
475 return NET_XMIT_SUCCESS;
476 }
477
478 return qdisc_drop(nskb, sch);
479}
480
481static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
482{
483 struct fifo_sched_data *q = qdisc_priv(sch);
484
485 if (opt) {
486 struct tc_fifo_qopt *ctl = RTA_DATA(opt);
487 if (RTA_PAYLOAD(opt) < sizeof(*ctl))
488 return -EINVAL;
489
490 q->limit = ctl->limit;
491 } else
492 q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
493
494 return 0;
495}
496
497static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb)
498{
499 struct fifo_sched_data *q = qdisc_priv(sch);
500 struct tc_fifo_qopt opt = { .limit = q->limit };
501
502 RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
503 return skb->len;
504
505rtattr_failure:
506 return -1;
507}
508
509static struct Qdisc_ops tfifo_qdisc_ops = {
510 .id = "tfifo",
511 .priv_size = sizeof(struct fifo_sched_data),
512 .enqueue = tfifo_enqueue,
513 .dequeue = qdisc_dequeue_head,
514 .requeue = qdisc_requeue,
515 .drop = qdisc_queue_drop,
516 .init = tfifo_init,
517 .reset = qdisc_reset_queue,
518 .change = tfifo_init,
519 .dump = tfifo_dump,
520};
521
439static int netem_init(struct Qdisc *sch, struct rtattr *opt) 522static int netem_init(struct Qdisc *sch, struct rtattr *opt)
440{ 523{
441 struct netem_sched_data *q = qdisc_priv(sch); 524 struct netem_sched_data *q = qdisc_priv(sch);
@@ -448,7 +531,7 @@ static int netem_init(struct Qdisc *sch, struct rtattr *opt)
448 q->timer.function = netem_watchdog; 531 q->timer.function = netem_watchdog;
449 q->timer.data = (unsigned long) sch; 532 q->timer.data = (unsigned long) sch;
450 533
451 q->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); 534 q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops);
452 if (!q->qdisc) { 535 if (!q->qdisc) {
453 pr_debug("netem: qdisc create failed\n"); 536 pr_debug("netem: qdisc create failed\n");
454 return -ENOMEM; 537 return -ENOMEM;