| From: Jakub Kicinski <jakub.kicinski@netronome.com> |
| Date: Mon, 17 Jun 2019 11:11:10 -0700 |
| Subject: net: netem: fix backlog accounting for corrupted GSO frames |
| |
| commit 177b8007463c4f36c9a2c7ce7aa9875a4cad9bd5 upstream. |
| |
| 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> |
| Acked-by: Cong Wang <xiyou.wangcong@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| net/sched/sch_netem.c | 13 ++++++++----- |
| 1 file changed, 8 insertions(+), 5 deletions(-) |
| |
| --- a/net/sched/sch_netem.c |
| +++ b/net/sched/sch_netem.c |
| @@ -440,8 +440,7 @@ static int netem_enqueue(struct sk_buff |
| 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; |
| @@ -495,6 +494,7 @@ static int netem_enqueue(struct sk_buff |
| segs = netem_segment(skb, sch); |
| if (!segs) |
| return rc_drop; |
| + qdisc_skb_cb(segs)->pkt_len = segs->len; |
| } else { |
| segs = skb; |
| } |
| @@ -575,6 +575,11 @@ static int netem_enqueue(struct sk_buff |
| |
| finish_segs: |
| if (segs) { |
| + unsigned int len, last_len; |
| + int nb = 0; |
| + |
| + len = skb->len; |
| + |
| while (segs) { |
| skb2 = segs->next; |
| segs->next = NULL; |
| @@ -590,9 +595,7 @@ finish_segs: |
| } |
| 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; |
| } |