aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2014-06-04 20:20:02 -0400
committerDavid S. Miller <davem@davemloft.net>2014-06-05 01:46:38 -0400
commit7e2b10c1e52ca37fb522be49f4be367f9311d0cd (patch)
tree67fd8a9e9b90f732c45f406df145c8a149d24a53
parent77157e1973cbdb8d60bdb0ec749d6014bedc5bd5 (diff)
net: Support for multiple checksums with gso
When creating a GSO packet segment we may need to set more than one checksum in the packet (for instance a TCP checksum and UDP checksum for VXLAN encapsulation). To be efficient, we want to do checksum calculation for any part of the packet at most once. This patch adds csum_start offset to skb_gso_cb. This tracks the starting offset for skb->csum which is initially set in skb_segment. When a protocol needs to compute a transport checksum it calls gso_make_checksum which computes the checksum value from the start of transport header to csum_start and then adds in skb->csum to get the full checksum. skb->csum and csum_start are then updated to reflect the checksum of the resultant packet starting from the transport header. This patch also adds a flag to skbuff, encap_hdr_csum, which is set in *gso_segment fucntions to indicate that a tunnel protocol needs checksum calculation Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/skbuff.h26
-rw-r--r--net/core/skbuff.c8
-rw-r--r--net/ipv4/ip_tunnel_core.c8
3 files changed, 40 insertions, 2 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 7a9beeb1c458..d8d397acb52c 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -567,7 +567,8 @@ struct sk_buff {
567 * headers if needed 567 * headers if needed
568 */ 568 */
569 __u8 encapsulation:1; 569 __u8 encapsulation:1;
570 /* 6/8 bit hole (depending on ndisc_nodetype presence) */ 570 __u8 encap_hdr_csum:1;
571 /* 5/7 bit hole (depending on ndisc_nodetype presence) */
571 kmemcheck_bitfield_end(flags2); 572 kmemcheck_bitfield_end(flags2);
572 573
573#if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL 574#if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
@@ -2988,6 +2989,7 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
2988struct skb_gso_cb { 2989struct skb_gso_cb {
2989 int mac_offset; 2990 int mac_offset;
2990 int encap_level; 2991 int encap_level;
2992 __u16 csum_start;
2991}; 2993};
2992#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb) 2994#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)
2993 2995
@@ -3012,6 +3014,28 @@ static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
3012 return 0; 3014 return 0;
3013} 3015}
3014 3016
3017/* Compute the checksum for a gso segment. First compute the checksum value
3018 * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and
3019 * then add in skb->csum (checksum from csum_start to end of packet).
3020 * skb->csum and csum_start are then updated to reflect the checksum of the
3021 * resultant packet starting from the transport header-- the resultant checksum
3022 * is in the res argument (i.e. normally zero or ~ of checksum of a pseudo
3023 * header.
3024 */
3025static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res)
3026{
3027 int plen = SKB_GSO_CB(skb)->csum_start - skb_headroom(skb) -
3028 skb_transport_offset(skb);
3029 __u16 csum;
3030
3031 csum = csum_fold(csum_partial(skb_transport_header(skb),
3032 plen, skb->csum));
3033 skb->csum = res;
3034 SKB_GSO_CB(skb)->csum_start -= plen;
3035
3036 return csum;
3037}
3038
3015static inline bool skb_is_gso(const struct sk_buff *skb) 3039static inline bool skb_is_gso(const struct sk_buff *skb)
3016{ 3040{
3017 return skb_shinfo(skb)->gso_size; 3041 return skb_shinfo(skb)->gso_size;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 3f6c7e8be8a4..05f4bef2ce12 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2885,7 +2885,9 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
2885 if (unlikely(!proto)) 2885 if (unlikely(!proto))
2886 return ERR_PTR(-EINVAL); 2886 return ERR_PTR(-EINVAL);
2887 2887
2888 csum = !!can_checksum_protocol(features, proto); 2888 csum = !head_skb->encap_hdr_csum &&
2889 !!can_checksum_protocol(features, proto);
2890
2889 __skb_push(head_skb, doffset); 2891 __skb_push(head_skb, doffset);
2890 headroom = skb_headroom(head_skb); 2892 headroom = skb_headroom(head_skb);
2891 pos = skb_headlen(head_skb); 2893 pos = skb_headlen(head_skb);
@@ -2983,6 +2985,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
2983 nskb->csum = skb_copy_and_csum_bits(head_skb, offset, 2985 nskb->csum = skb_copy_and_csum_bits(head_skb, offset,
2984 skb_put(nskb, len), 2986 skb_put(nskb, len),
2985 len, 0); 2987 len, 0);
2988 SKB_GSO_CB(nskb)->csum_start =
2989 skb_headroom(nskb) + offset;
2986 continue; 2990 continue;
2987 } 2991 }
2988 2992
@@ -3052,6 +3056,8 @@ perform_csum_check:
3052 nskb->csum = skb_checksum(nskb, doffset, 3056 nskb->csum = skb_checksum(nskb, doffset,
3053 nskb->len - doffset, 0); 3057 nskb->len - doffset, 0);
3054 nskb->ip_summed = CHECKSUM_NONE; 3058 nskb->ip_summed = CHECKSUM_NONE;
3059 SKB_GSO_CB(nskb)->csum_start =
3060 skb_headroom(nskb) + doffset;
3055 } 3061 }
3056 } while ((offset += len) < head_skb->len); 3062 } while ((offset += len) < head_skb->len);
3057 3063
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 847e69cbff7e..f4c987bb7e94 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -135,6 +135,14 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
135 return skb; 135 return skb;
136 } 136 }
137 137
138 /* If packet is not gso and we are resolving any partial checksum,
139 * clear encapsulation flag. This allows setting CHECKSUM_PARTIAL
140 * on the outer header without confusing devices that implement
141 * NETIF_F_IP_CSUM with encapsulation.
142 */
143 if (csum_help)
144 skb->encapsulation = 0;
145
138 if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) { 146 if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
139 err = skb_checksum_help(skb); 147 err = skb_checksum_help(skb);
140 if (unlikely(err)) 148 if (unlikely(err))