]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
[NET]: Fix CHECKSUM_HW GSO problems.
authorHerbert Xu <herbert@gondor.apana.org.au>
Mon, 26 Jun 2006 06:55:46 +0000 (23:55 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 26 Jun 2006 06:55:46 +0000 (23:55 -0700)
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>
net/ipv4/tcp.c

index 0e029c4e2903e4353fa46568bdedc599ca3e2209..c04176be7ed12826de51bcf036d2e64cd7f0b17e 100644 (file)
@@ -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;