| From a07154963ae35fe5e2043a9c40086cda1b5963ef Mon Sep 17 00:00:00 2001 |
| From: Herbert Xu <herbert@gondor.apana.org.au> |
| Date: Tue, 10 Jan 2017 12:24:15 -0800 |
| Subject: [PATCH] gro: Disable frag0 optimization on IPv6 ext headers |
| |
| commit 57ea52a865144aedbcd619ee0081155e658b6f7d upstream. |
| |
| The GRO fast path caches the frag0 address. This address becomes |
| invalid if frag0 is modified by pskb_may_pull or its variants. |
| So whenever that happens we must disable the frag0 optimization. |
| |
| This is usually done through the combination of gro_header_hard |
| and gro_header_slow, however, the IPv6 extension header path did |
| the pulling directly and would continue to use the GRO fast path |
| incorrectly. |
| |
| This patch fixes it by disabling the fast path when we enter the |
| IPv6 extension header path. |
| |
| Fixes: 78a478d0efd9 ("gro: Inline skb_gro_header and cache frag0 virtual address") |
| Reported-by: Slava Shwartsman <slavash@mellanox.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h |
| index e942c67ea230..280a9e701161 100644 |
| --- a/include/linux/netdevice.h |
| +++ b/include/linux/netdevice.h |
| @@ -2487,14 +2487,19 @@ static inline int skb_gro_header_hard(struct sk_buff *skb, unsigned int hlen) |
| return NAPI_GRO_CB(skb)->frag0_len < hlen; |
| } |
| |
| +static inline void skb_gro_frag0_invalidate(struct sk_buff *skb) |
| +{ |
| + NAPI_GRO_CB(skb)->frag0 = NULL; |
| + NAPI_GRO_CB(skb)->frag0_len = 0; |
| +} |
| + |
| static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen, |
| unsigned int offset) |
| { |
| if (!pskb_may_pull(skb, hlen)) |
| return NULL; |
| |
| - NAPI_GRO_CB(skb)->frag0 = NULL; |
| - NAPI_GRO_CB(skb)->frag0_len = 0; |
| + skb_gro_frag0_invalidate(skb); |
| return skb->data + offset; |
| } |
| |
| diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c |
| index 93294cf4525c..fdf875270f97 100644 |
| --- a/net/ipv6/ip6_offload.c |
| +++ b/net/ipv6/ip6_offload.c |
| @@ -188,6 +188,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, |
| ops = rcu_dereference(inet6_offloads[proto]); |
| if (!ops || !ops->callbacks.gro_receive) { |
| __pskb_pull(skb, skb_gro_offset(skb)); |
| + skb_gro_frag0_invalidate(skb); |
| proto = ipv6_gso_pull_exthdrs(skb, proto); |
| skb_gro_pull(skb, -skb_transport_offset(skb)); |
| skb_reset_transport_header(skb); |
| -- |
| 2.10.1 |
| |