Message ID | 20190617181111.5025-2-jakub.kicinski@netronome.com |
---|---|
State | Accepted |
Delegated to: | David Miller |
Headers | show |
Series | net: netem: fix issues with corrupting GSO frames | expand |
On Mon, Jun 17, 2019 at 11:11 AM Jakub Kicinski <jakub.kicinski@netronome.com> wrote: > > When GSO frame has to be corrupted netem uses skb_gso_segment() > to produce the list of frames, and re-enqueues the segments one > by one. The backlog length has to be adjusted to account for > new frames. > > The current calculation is incorrect, leading to wrong backlog > lengths in the parent qdisc (both bytes and packets), and > incorrect packet backlog count in netem itself. > > Parent backlog goes negative, netem's packet backlog counts > all non-first segments twice (thus remaining non-zero even > after qdisc is emptied). > > Move the variables used to count the adjustment into local > scope to make 100% sure they aren't used at any stage in > backports. > > Fixes: 6071bd1aa13e ("netem: Segment GSO packets on enqueue") > Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> > Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com> Looks good! Acked-by: Cong Wang <xiyou.wangcong@gmail.com>
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 956ff3da81f4..3b3e2d772c3b 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -439,8 +439,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct netem_skb_cb *cb; struct sk_buff *skb2; struct sk_buff *segs = NULL; - unsigned int len = 0, last_len, prev_len = qdisc_pkt_len(skb); - int nb = 0; + unsigned int prev_len = qdisc_pkt_len(skb); int count = 1; int rc = NET_XMIT_SUCCESS; int rc_drop = NET_XMIT_DROP; @@ -497,6 +496,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, segs = netem_segment(skb, sch, to_free); if (!segs) return rc_drop; + qdisc_skb_cb(segs)->pkt_len = segs->len; } else { segs = skb; } @@ -593,6 +593,11 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, finish_segs: if (segs) { + unsigned int len, last_len; + int nb = 0; + + len = skb->len; + while (segs) { skb2 = segs->next; skb_mark_not_on_list(segs); @@ -608,9 +613,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, } segs = skb2; } - sch->q.qlen += nb; - if (nb > 1) - qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len); + qdisc_tree_reduce_backlog(sch, -nb, prev_len - len); } return NET_XMIT_SUCCESS; }