net: pskb_trim_rcsum() and CHECKSUM_COMPLETE are friends
authorEric Dumazet <edumazet@google.com>
Thu, 13 Sep 2018 14:58:56 +0000 (07:58 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 19 Sep 2018 20:43:47 +0000 (22:43 +0200)
After working on IP defragmentation lately, I found that some large
packets defeat CHECKSUM_COMPLETE optimization because of NIC adding
zero paddings on the last (small) fragment.

While removing the padding with pskb_trim_rcsum(), we set skb->ip_summed
to CHECKSUM_NONE, forcing a full csum validation, even if all prior
fragments had CHECKSUM_COMPLETE set.

We can instead compute the checksum of the part we are trimming,
usually smaller than the part we keep.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit 88078d98d1bb085d72af8437707279e203524fa5)
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/skbuff.h
net/core/skbuff.c

index 9c84573..758084b 100644 (file)
@@ -3135,6 +3135,7 @@ static inline void *skb_push_rcsum(struct sk_buff *skb, unsigned int len)
        return skb->data;
 }
 
+int pskb_trim_rcsum_slow(struct sk_buff *skb, unsigned int len);
 /**
  *     pskb_trim_rcsum - trim received skb and update checksum
  *     @skb: buffer to trim
@@ -3148,9 +3149,7 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
 {
        if (likely(len >= skb->len))
                return 0;
-       if (skb->ip_summed == CHECKSUM_COMPLETE)
-               skb->ip_summed = CHECKSUM_NONE;
-       return __pskb_trim(skb, len);
+       return pskb_trim_rcsum_slow(skb, len);
 }
 
 static inline int __skb_trim_rcsum(struct sk_buff *skb, unsigned int len)
index 8fd690d..168a3e8 100644 (file)
@@ -1839,6 +1839,20 @@ done:
 }
 EXPORT_SYMBOL(___pskb_trim);
 
+/* Note : use pskb_trim_rcsum() instead of calling this directly
+ */
+int pskb_trim_rcsum_slow(struct sk_buff *skb, unsigned int len)
+{
+       if (skb->ip_summed == CHECKSUM_COMPLETE) {
+               int delta = skb->len - len;
+
+               skb->csum = csum_sub(skb->csum,
+                                    skb_checksum(skb, len, delta, 0));
+       }
+       return __pskb_trim(skb, len);
+}
+EXPORT_SYMBOL(pskb_trim_rcsum_slow);
+
 /**
  *     __pskb_pull_tail - advance tail of skb header
  *     @skb: buffer to reallocate