forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									77157e1973
								
							
						
					
					
						commit
						7e2b10c1e5
					
				
					 3 changed files with 40 additions and 2 deletions
				
			
		| 
						 | 
					@ -567,7 +567,8 @@ struct sk_buff {
 | 
				
			||||||
	 * headers if needed
 | 
						 * headers if needed
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	__u8			encapsulation:1;
 | 
						__u8			encapsulation:1;
 | 
				
			||||||
	/* 6/8 bit hole (depending on ndisc_nodetype presence) */
 | 
						__u8			encap_hdr_csum:1;
 | 
				
			||||||
 | 
						/* 5/7 bit hole (depending on ndisc_nodetype presence) */
 | 
				
			||||||
	kmemcheck_bitfield_end(flags2);
 | 
						kmemcheck_bitfield_end(flags2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
 | 
					#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)
 | 
				
			||||||
struct skb_gso_cb {
 | 
					struct skb_gso_cb {
 | 
				
			||||||
	int	mac_offset;
 | 
						int	mac_offset;
 | 
				
			||||||
	int	encap_level;
 | 
						int	encap_level;
 | 
				
			||||||
 | 
						__u16	csum_start;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)
 | 
					#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3012,6 +3014,28 @@ static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Compute the checksum for a gso segment. First compute the checksum value
 | 
				
			||||||
 | 
					 * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and
 | 
				
			||||||
 | 
					 * then add in skb->csum (checksum from csum_start to end of packet).
 | 
				
			||||||
 | 
					 * skb->csum and csum_start are then updated to reflect the checksum of the
 | 
				
			||||||
 | 
					 * resultant packet starting from the transport header-- the resultant checksum
 | 
				
			||||||
 | 
					 * is in the res argument (i.e. normally zero or ~ of checksum of a pseudo
 | 
				
			||||||
 | 
					 * header.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int plen = SKB_GSO_CB(skb)->csum_start - skb_headroom(skb) -
 | 
				
			||||||
 | 
						    skb_transport_offset(skb);
 | 
				
			||||||
 | 
						__u16 csum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						csum = csum_fold(csum_partial(skb_transport_header(skb),
 | 
				
			||||||
 | 
									      plen, skb->csum));
 | 
				
			||||||
 | 
						skb->csum = res;
 | 
				
			||||||
 | 
						SKB_GSO_CB(skb)->csum_start -= plen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return csum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool skb_is_gso(const struct sk_buff *skb)
 | 
					static inline bool skb_is_gso(const struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return skb_shinfo(skb)->gso_size;
 | 
						return skb_shinfo(skb)->gso_size;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2885,7 +2885,9 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
 | 
				
			||||||
	if (unlikely(!proto))
 | 
						if (unlikely(!proto))
 | 
				
			||||||
		return ERR_PTR(-EINVAL);
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	csum = !!can_checksum_protocol(features, proto);
 | 
						csum = !head_skb->encap_hdr_csum &&
 | 
				
			||||||
 | 
						    !!can_checksum_protocol(features, proto);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	__skb_push(head_skb, doffset);
 | 
						__skb_push(head_skb, doffset);
 | 
				
			||||||
	headroom = skb_headroom(head_skb);
 | 
						headroom = skb_headroom(head_skb);
 | 
				
			||||||
	pos = skb_headlen(head_skb);
 | 
						pos = skb_headlen(head_skb);
 | 
				
			||||||
| 
						 | 
					@ -2983,6 +2985,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
 | 
				
			||||||
			nskb->csum = skb_copy_and_csum_bits(head_skb, offset,
 | 
								nskb->csum = skb_copy_and_csum_bits(head_skb, offset,
 | 
				
			||||||
							    skb_put(nskb, len),
 | 
												    skb_put(nskb, len),
 | 
				
			||||||
							    len, 0);
 | 
												    len, 0);
 | 
				
			||||||
 | 
								SKB_GSO_CB(nskb)->csum_start =
 | 
				
			||||||
 | 
								    skb_headroom(nskb) + offset;
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3052,6 +3056,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
 | 
				
			||||||
			nskb->csum = skb_checksum(nskb, doffset,
 | 
								nskb->csum = skb_checksum(nskb, doffset,
 | 
				
			||||||
						  nskb->len - doffset, 0);
 | 
											  nskb->len - doffset, 0);
 | 
				
			||||||
			nskb->ip_summed = CHECKSUM_NONE;
 | 
								nskb->ip_summed = CHECKSUM_NONE;
 | 
				
			||||||
 | 
								SKB_GSO_CB(nskb)->csum_start =
 | 
				
			||||||
 | 
								    skb_headroom(nskb) + doffset;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} while ((offset += len) < head_skb->len);
 | 
						} while ((offset += len) < head_skb->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -135,6 +135,14 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
 | 
				
			||||||
		return skb;
 | 
							return skb;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If packet is not gso and we are resolving any partial checksum,
 | 
				
			||||||
 | 
						 * clear encapsulation flag. This allows setting CHECKSUM_PARTIAL
 | 
				
			||||||
 | 
						 * on the outer header without confusing devices that implement
 | 
				
			||||||
 | 
						 * NETIF_F_IP_CSUM with encapsulation.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (csum_help)
 | 
				
			||||||
 | 
							skb->encapsulation = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
 | 
						if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
 | 
				
			||||||
		err = skb_checksum_help(skb);
 | 
							err = skb_checksum_help(skb);
 | 
				
			||||||
		if (unlikely(err))
 | 
							if (unlikely(err))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue