Commit 7a7a9bd7 authored by Ilan Tayari's avatar Ilan Tayari Committed by David S. Miller

gso: Validate assumption of frag_list segementation

Commit 07b26c94 ("gso: Support partial splitting at the frag_list
pointer") assumes that all SKBs in a frag_list (except maybe the last
one) contain the same amount of GSO payload.

This assumption is not always correct, resulting in the following
warning message in the log:
    skb_segment: too many frags

For example, mlx5 driver in Striding RQ mode creates some RX SKBs with
one frag, and some with 2 frags.
After GRO, the frag_list SKBs end up having different amounts of payload.
If this frag_list SKB is then forwarded, the aforementioned assumption
is violated.

Validate the assumption, and fall back to software GSO if it not true.

Fixes: 07b26c94 ("gso: Support partial splitting at the frag_list pointer")
Signed-off-by: default avatarIlan Tayari <ilant@mellanox.com>
Signed-off-by: default avatarIlya Lesokhin <ilyal@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent edb12f2d
...@@ -3082,22 +3082,32 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, ...@@ -3082,22 +3082,32 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
if (sg && csum && (mss != GSO_BY_FRAGS)) { if (sg && csum && (mss != GSO_BY_FRAGS)) {
if (!(features & NETIF_F_GSO_PARTIAL)) { if (!(features & NETIF_F_GSO_PARTIAL)) {
struct sk_buff *iter; struct sk_buff *iter;
unsigned int frag_len;
if (!list_skb || if (!list_skb ||
!net_gso_ok(features, skb_shinfo(head_skb)->gso_type)) !net_gso_ok(features, skb_shinfo(head_skb)->gso_type))
goto normal; goto normal;
/* Split the buffer at the frag_list pointer. /* If we get here then all the required
* This is based on the assumption that all * GSO features except frag_list are supported.
* buffers in the chain excluding the last * Try to split the SKB to multiple GSO SKBs
* containing the same amount of data. * with no frag_list.
* Currently we can do that only when the buffers don't
* have a linear part and all the buffers except
* the last are of the same length.
*/ */
frag_len = list_skb->len;
skb_walk_frags(head_skb, iter) { skb_walk_frags(head_skb, iter) {
if (frag_len != iter->len && iter->next)
goto normal;
if (skb_headlen(iter) && !iter->head_frag) if (skb_headlen(iter) && !iter->head_frag)
goto normal; goto normal;
len -= iter->len; len -= iter->len;
} }
if (len != frag_len)
goto normal;
} }
/* GSO partial only requires that we trim off any excess that /* GSO partial only requires that we trim off any excess that
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment