[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))
|
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;
|
||||||
|
|
Loading…
Reference in a new issue