[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:
Herbert Xu 2006-06-25 23:55:46 -07:00 committed by David S. Miller
parent 3ba07e65b2
commit 0718bcc09b

View file

@ -2166,7 +2166,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
if (!pskb_may_pull(skb, thlen)) if (!pskb_may_pull(skb, thlen))
goto out; goto out;
oldlen = ~htonl(skb->len); oldlen = (u16)~skb->len;
__skb_pull(skb, thlen); __skb_pull(skb, thlen);
segs = skb_segment(skb, sg); segs = skb_segment(skb, sg);
@ -2174,7 +2174,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
goto out; goto out;
len = skb_shinfo(skb)->gso_size; len = skb_shinfo(skb)->gso_size;
delta = csum_add(oldlen, htonl(thlen + len)); delta = htonl(oldlen + (thlen + len));
skb = segs; skb = segs;
th = skb->h.th; th = skb->h.th;
@ -2183,10 +2183,10 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
do { do {
th->fin = th->psh = 0; th->fin = th->psh = 0;
if (skb->ip_summed == CHECKSUM_NONE) { th->check = ~csum_fold(th->check + delta);
th->check = csum_fold(csum_partial( if (skb->ip_summed != CHECKSUM_HW)
skb->h.raw, thlen, csum_add(skb->csum, delta))); th->check = csum_fold(csum_partial(skb->h.raw, thlen,
} skb->csum));
seq += len; seq += len;
skb = skb->next; skb = skb->next;
@ -2196,11 +2196,11 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
th->cwr = 0; th->cwr = 0;
} while (skb->next); } while (skb->next);
if (skb->ip_summed == CHECKSUM_NONE) { delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len);
delta = csum_add(oldlen, htonl(skb->tail - skb->h.raw)); th->check = ~csum_fold(th->check + delta);
th->check = csum_fold(csum_partial( if (skb->ip_summed != CHECKSUM_HW)
skb->h.raw, thlen, csum_add(skb->csum, delta))); th->check = csum_fold(csum_partial(skb->h.raw, thlen,
} skb->csum));
out: out:
return segs; return segs;