Skip to content

Commit 5061253

Browse files
Eric Dumazetdavem330
authored andcommitted
netem: fix classful handling
Commit 10f6dfc (Revert "sch_netem: Remove classful functionality") reintroduced classful functionality to netem, but broke basic netem behavior : netem uses an t(ime)fifo queue, and store timestamps in skb->cb[] If qdisc is changed, time constraints are not respected and other qdisc can destroy skb->cb[] and block netem at dequeue time. Fix this by always using internal tfifo, and optionally attach a child qdisc to netem (or a tree of qdiscs) Example of use : DEV=eth3 tc qdisc del dev $DEV root tc qdisc add dev $DEV root handle 30: est 1sec 8sec netem delay 20ms 10ms tc qdisc add dev $DEV handle 40:0 parent 30:0 tbf \ burst 20480 limit 20480 mtu 1514 rate 32000bps qdisc netem 30: root refcnt 18 limit 1000 delay 20.0ms 10.0ms Sent 190792 bytes 413 pkt (dropped 0, overlimits 0 requeues 0) rate 18416bit 3pps backlog 0b 0p requeues 0 qdisc tbf 40: parent 30: rate 256000bit burst 20Kb/8 mpu 0b lat 0us Sent 190792 bytes 413 pkt (dropped 6, overlimits 10 requeues 0) backlog 0b 5p requeues 0 Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> CC: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 32b293a commit 5061253

File tree

1 file changed

+81
-121
lines changed

1 file changed

+81
-121
lines changed

net/sched/sch_netem.c

Lines changed: 81 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@
6767
*/
6868

6969
struct netem_sched_data {
70+
/* internal t(ime)fifo qdisc uses sch->q and sch->limit */
71+
72+
/* optional qdisc for classful handling (NULL at netem init) */
7073
struct Qdisc *qdisc;
74+
7175
struct qdisc_watchdog watchdog;
7276

7377
psched_tdiff_t latency;
@@ -117,7 +121,9 @@ struct netem_sched_data {
117121

118122
};
119123

120-
/* Time stamp put into socket buffer control block */
124+
/* Time stamp put into socket buffer control block
125+
* Only valid when skbs are in our internal t(ime)fifo queue.
126+
*/
121127
struct netem_skb_cb {
122128
psched_time_t time_to_send;
123129
};
@@ -324,6 +330,31 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche
324330
return PSCHED_NS2TICKS(ticks);
325331
}
326332

333+
static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
334+
{
335+
struct sk_buff_head *list = &sch->q;
336+
psched_time_t tnext = netem_skb_cb(nskb)->time_to_send;
337+
struct sk_buff *skb;
338+
339+
if (likely(skb_queue_len(list) < sch->limit)) {
340+
skb = skb_peek_tail(list);
341+
/* Optimize for add at tail */
342+
if (likely(!skb || tnext >= netem_skb_cb(skb)->time_to_send))
343+
return qdisc_enqueue_tail(nskb, sch);
344+
345+
skb_queue_reverse_walk(list, skb) {
346+
if (tnext >= netem_skb_cb(skb)->time_to_send)
347+
break;
348+
}
349+
350+
__skb_queue_after(list, skb, nskb);
351+
sch->qstats.backlog += qdisc_pkt_len(nskb);
352+
return NET_XMIT_SUCCESS;
353+
}
354+
355+
return qdisc_reshape_fail(nskb, sch);
356+
}
357+
327358
/*
328359
* Insert one skb into qdisc.
329360
* Note: parent depends on return value to account for queue length.
@@ -399,7 +430,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
399430
now = psched_get_time();
400431

401432
if (q->rate) {
402-
struct sk_buff_head *list = &q->qdisc->q;
433+
struct sk_buff_head *list = &sch->q;
403434

404435
delay += packet_len_2_sched_time(skb->len, q);
405436

@@ -417,7 +448,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
417448

418449
cb->time_to_send = now + delay;
419450
++q->counter;
420-
ret = qdisc_enqueue(skb, q->qdisc);
451+
ret = tfifo_enqueue(skb, sch);
421452
} else {
422453
/*
423454
* Do re-ordering by putting one out of N packets at the front
@@ -426,7 +457,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
426457
cb->time_to_send = psched_get_time();
427458
q->counter = 0;
428459

429-
__skb_queue_head(&q->qdisc->q, skb);
460+
__skb_queue_head(&sch->q, skb);
430461
q->qdisc->qstats.backlog += qdisc_pkt_len(skb);
431462
q->qdisc->qstats.requeues++;
432463
ret = NET_XMIT_SUCCESS;
@@ -439,19 +470,20 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
439470
}
440471
}
441472

442-
sch->q.qlen++;
443473
return NET_XMIT_SUCCESS;
444474
}
445475

446476
static unsigned int netem_drop(struct Qdisc *sch)
447477
{
448478
struct netem_sched_data *q = qdisc_priv(sch);
449-
unsigned int len = 0;
479+
unsigned int len;
450480

451-
if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
452-
sch->q.qlen--;
481+
len = qdisc_queue_drop(sch);
482+
if (!len && q->qdisc && q->qdisc->ops->drop)
483+
len = q->qdisc->ops->drop(q->qdisc);
484+
if (len)
453485
sch->qstats.drops++;
454-
}
486+
455487
return len;
456488
}
457489

@@ -463,16 +495,16 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
463495
if (qdisc_is_throttled(sch))
464496
return NULL;
465497

466-
skb = q->qdisc->ops->peek(q->qdisc);
498+
tfifo_dequeue:
499+
skb = qdisc_peek_head(sch);
467500
if (skb) {
468501
const struct netem_skb_cb *cb = netem_skb_cb(skb);
469-
psched_time_t now = psched_get_time();
470502

471503
/* if more time remaining? */
472-
if (cb->time_to_send <= now) {
473-
skb = qdisc_dequeue_peeked(q->qdisc);
504+
if (cb->time_to_send <= psched_get_time()) {
505+
skb = qdisc_dequeue_tail(sch);
474506
if (unlikely(!skb))
475-
return NULL;
507+
goto qdisc_dequeue;
476508

477509
#ifdef CONFIG_NET_CLS_ACT
478510
/*
@@ -483,24 +515,47 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
483515
skb->tstamp.tv64 = 0;
484516
#endif
485517

486-
sch->q.qlen--;
518+
if (q->qdisc) {
519+
int err = qdisc_enqueue(skb, q->qdisc);
520+
521+
if (unlikely(err != NET_XMIT_SUCCESS)) {
522+
if (net_xmit_drop_count(err)) {
523+
sch->qstats.drops++;
524+
qdisc_tree_decrease_qlen(sch, 1);
525+
}
526+
}
527+
goto tfifo_dequeue;
528+
}
529+
deliver:
487530
qdisc_unthrottled(sch);
488531
qdisc_bstats_update(sch, skb);
489532
return skb;
490533
}
491534

535+
if (q->qdisc) {
536+
skb = q->qdisc->ops->dequeue(q->qdisc);
537+
if (skb)
538+
goto deliver;
539+
}
492540
qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send);
493541
}
494542

543+
qdisc_dequeue:
544+
if (q->qdisc) {
545+
skb = q->qdisc->ops->dequeue(q->qdisc);
546+
if (skb)
547+
goto deliver;
548+
}
495549
return NULL;
496550
}
497551

498552
static void netem_reset(struct Qdisc *sch)
499553
{
500554
struct netem_sched_data *q = qdisc_priv(sch);
501555

502-
qdisc_reset(q->qdisc);
503-
sch->q.qlen = 0;
556+
qdisc_reset_queue(sch);
557+
if (q->qdisc)
558+
qdisc_reset(q->qdisc);
504559
qdisc_watchdog_cancel(&q->watchdog);
505560
}
506561

@@ -690,11 +745,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
690745
if (ret < 0)
691746
return ret;
692747

693-
ret = fifo_set_limit(q->qdisc, qopt->limit);
694-
if (ret) {
695-
pr_info("netem: can't set fifo limit\n");
696-
return ret;
697-
}
748+
sch->limit = qopt->limit;
698749

699750
q->latency = qopt->latency;
700751
q->jitter = qopt->jitter;
@@ -735,88 +786,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
735786
return ret;
736787
}
737788

738-
/*
739-
* Special case version of FIFO queue for use by netem.
740-
* It queues in order based on timestamps in skb's
741-
*/
742-
struct fifo_sched_data {
743-
u32 limit;
744-
psched_time_t oldest;
745-
};
746-
747-
static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
748-
{
749-
struct fifo_sched_data *q = qdisc_priv(sch);
750-
struct sk_buff_head *list = &sch->q;
751-
psched_time_t tnext = netem_skb_cb(nskb)->time_to_send;
752-
struct sk_buff *skb;
753-
754-
if (likely(skb_queue_len(list) < q->limit)) {
755-
/* Optimize for add at tail */
756-
if (likely(skb_queue_empty(list) || tnext >= q->oldest)) {
757-
q->oldest = tnext;
758-
return qdisc_enqueue_tail(nskb, sch);
759-
}
760-
761-
skb_queue_reverse_walk(list, skb) {
762-
const struct netem_skb_cb *cb = netem_skb_cb(skb);
763-
764-
if (tnext >= cb->time_to_send)
765-
break;
766-
}
767-
768-
__skb_queue_after(list, skb, nskb);
769-
770-
sch->qstats.backlog += qdisc_pkt_len(nskb);
771-
772-
return NET_XMIT_SUCCESS;
773-
}
774-
775-
return qdisc_reshape_fail(nskb, sch);
776-
}
777-
778-
static int tfifo_init(struct Qdisc *sch, struct nlattr *opt)
779-
{
780-
struct fifo_sched_data *q = qdisc_priv(sch);
781-
782-
if (opt) {
783-
struct tc_fifo_qopt *ctl = nla_data(opt);
784-
if (nla_len(opt) < sizeof(*ctl))
785-
return -EINVAL;
786-
787-
q->limit = ctl->limit;
788-
} else
789-
q->limit = max_t(u32, qdisc_dev(sch)->tx_queue_len, 1);
790-
791-
q->oldest = PSCHED_PASTPERFECT;
792-
return 0;
793-
}
794-
795-
static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb)
796-
{
797-
struct fifo_sched_data *q = qdisc_priv(sch);
798-
struct tc_fifo_qopt opt = { .limit = q->limit };
799-
800-
NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
801-
return skb->len;
802-
803-
nla_put_failure:
804-
return -1;
805-
}
806-
807-
static struct Qdisc_ops tfifo_qdisc_ops __read_mostly = {
808-
.id = "tfifo",
809-
.priv_size = sizeof(struct fifo_sched_data),
810-
.enqueue = tfifo_enqueue,
811-
.dequeue = qdisc_dequeue_head,
812-
.peek = qdisc_peek_head,
813-
.drop = qdisc_queue_drop,
814-
.init = tfifo_init,
815-
.reset = qdisc_reset_queue,
816-
.change = tfifo_init,
817-
.dump = tfifo_dump,
818-
};
819-
820789
static int netem_init(struct Qdisc *sch, struct nlattr *opt)
821790
{
822791
struct netem_sched_data *q = qdisc_priv(sch);
@@ -828,18 +797,9 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt)
828797
qdisc_watchdog_init(&q->watchdog, sch);
829798

830799
q->loss_model = CLG_RANDOM;
831-
q->qdisc = qdisc_create_dflt(sch->dev_queue, &tfifo_qdisc_ops,
832-
TC_H_MAKE(sch->handle, 1));
833-
if (!q->qdisc) {
834-
pr_notice("netem: qdisc create tfifo qdisc failed\n");
835-
return -ENOMEM;
836-
}
837-
838800
ret = netem_change(sch, opt);
839-
if (ret) {
801+
if (ret)
840802
pr_info("netem: change failed\n");
841-
qdisc_destroy(q->qdisc);
842-
}
843803
return ret;
844804
}
845805

@@ -848,7 +808,8 @@ static void netem_destroy(struct Qdisc *sch)
848808
struct netem_sched_data *q = qdisc_priv(sch);
849809

850810
qdisc_watchdog_cancel(&q->watchdog);
851-
qdisc_destroy(q->qdisc);
811+
if (q->qdisc)
812+
qdisc_destroy(q->qdisc);
852813
dist_free(q->delay_dist);
853814
}
854815

@@ -952,7 +913,7 @@ static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
952913
{
953914
struct netem_sched_data *q = qdisc_priv(sch);
954915

955-
if (cl != 1) /* only one class */
916+
if (cl != 1 || !q->qdisc) /* only one class */
956917
return -ENOENT;
957918

958919
tcm->tcm_handle |= TC_H_MIN(1);
@@ -966,14 +927,13 @@ static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
966927
{
967928
struct netem_sched_data *q = qdisc_priv(sch);
968929

969-
if (new == NULL)
970-
new = &noop_qdisc;
971-
972930
sch_tree_lock(sch);
973931
*old = q->qdisc;
974932
q->qdisc = new;
975-
qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
976-
qdisc_reset(*old);
933+
if (*old) {
934+
qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
935+
qdisc_reset(*old);
936+
}
977937
sch_tree_unlock(sch);
978938

979939
return 0;

0 commit comments

Comments
 (0)