forked from mirrors/linux
		
	[NET]: Fix CHECKSUM_HW GSO problems.
Fix checksum problems in the GSO code path for CHECKSUM_HW packets. The ipv4 TCP pseudo header checksum has to be adjusted for GSO segmented packets. The adjustment is needed because the length field in the pseudo-header changes. However, because we have the inequality oldlen > newlen, we know that delta = (u16)~oldlen + newlen is still a 16-bit quantity. This also means that htonl(delta) + th->check still fits in 32 bits. Therefore we don't have to use csum_add on this operations. This is based on a patch by Michael Chan <mchan@broadcom.com>. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Acked-by: Michael Chan <mchan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3ba07e65b2
								
							
						
					
					
						commit
						0718bcc09b
					
				
					 1 changed files with 11 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -2166,7 +2166,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
 | 
			
		|||
	if (!pskb_may_pull(skb, thlen))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	oldlen = ~htonl(skb->len);
 | 
			
		||||
	oldlen = (u16)~skb->len;
 | 
			
		||||
	__skb_pull(skb, thlen);
 | 
			
		||||
 | 
			
		||||
	segs = skb_segment(skb, sg);
 | 
			
		||||
| 
						 | 
				
			
			@ -2174,7 +2174,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
 | 
			
		|||
		goto out;
 | 
			
		||||
 | 
			
		||||
	len = skb_shinfo(skb)->gso_size;
 | 
			
		||||
	delta = csum_add(oldlen, htonl(thlen + len));
 | 
			
		||||
	delta = htonl(oldlen + (thlen + len));
 | 
			
		||||
 | 
			
		||||
	skb = segs;
 | 
			
		||||
	th = skb->h.th;
 | 
			
		||||
| 
						 | 
				
			
			@ -2183,10 +2183,10 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
 | 
			
		|||
	do {
 | 
			
		||||
		th->fin = th->psh = 0;
 | 
			
		||||
 | 
			
		||||
		if (skb->ip_summed == CHECKSUM_NONE) {
 | 
			
		||||
			th->check = csum_fold(csum_partial(
 | 
			
		||||
				skb->h.raw, thlen, csum_add(skb->csum, delta)));
 | 
			
		||||
		}
 | 
			
		||||
		th->check = ~csum_fold(th->check + delta);
 | 
			
		||||
		if (skb->ip_summed != CHECKSUM_HW)
 | 
			
		||||
			th->check = csum_fold(csum_partial(skb->h.raw, thlen,
 | 
			
		||||
							   skb->csum));
 | 
			
		||||
 | 
			
		||||
		seq += len;
 | 
			
		||||
		skb = skb->next;
 | 
			
		||||
| 
						 | 
				
			
			@ -2196,11 +2196,11 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
 | 
			
		|||
		th->cwr = 0;
 | 
			
		||||
	} while (skb->next);
 | 
			
		||||
 | 
			
		||||
	if (skb->ip_summed == CHECKSUM_NONE) {
 | 
			
		||||
		delta = csum_add(oldlen, htonl(skb->tail - skb->h.raw));
 | 
			
		||||
		th->check = csum_fold(csum_partial(
 | 
			
		||||
			skb->h.raw, thlen, csum_add(skb->csum, delta)));
 | 
			
		||||
	}
 | 
			
		||||
	delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len);
 | 
			
		||||
	th->check = ~csum_fold(th->check + delta);
 | 
			
		||||
	if (skb->ip_summed != CHECKSUM_HW)
 | 
			
		||||
		th->check = csum_fold(csum_partial(skb->h.raw, thlen,
 | 
			
		||||
						   skb->csum));
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	return segs;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue