diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2006-06-26 02:55:46 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-06-26 02:55:46 -0400 |
commit | 0718bcc09b3597c51e87f265c72135a4928d3c0b (patch) | |
tree | fd4a686629488bad28f3c780e6b2b52803698877 /net | |
parent | 3ba07e65b288f00cc4d2420f1da46309b1cb5a0c (diff) |
[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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/tcp.c | 22 |
1 files changed, 11 insertions, 11 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0e029c4e2903..c04176be7ed1 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -2166,7 +2166,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg) | |||
2166 | if (!pskb_may_pull(skb, thlen)) | 2166 | if (!pskb_may_pull(skb, thlen)) |
2167 | goto out; | 2167 | goto out; |
2168 | 2168 | ||
2169 | oldlen = ~htonl(skb->len); | 2169 | oldlen = (u16)~skb->len; |
2170 | __skb_pull(skb, thlen); | 2170 | __skb_pull(skb, thlen); |
2171 | 2171 | ||
2172 | segs = skb_segment(skb, sg); | 2172 | segs = skb_segment(skb, sg); |
@@ -2174,7 +2174,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg) | |||
2174 | goto out; | 2174 | goto out; |
2175 | 2175 | ||
2176 | len = skb_shinfo(skb)->gso_size; | 2176 | len = skb_shinfo(skb)->gso_size; |
2177 | delta = csum_add(oldlen, htonl(thlen + len)); | 2177 | delta = htonl(oldlen + (thlen + len)); |
2178 | 2178 | ||
2179 | skb = segs; | 2179 | skb = segs; |
2180 | th = skb->h.th; | 2180 | th = skb->h.th; |
@@ -2183,10 +2183,10 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg) | |||
2183 | do { | 2183 | do { |
2184 | th->fin = th->psh = 0; | 2184 | th->fin = th->psh = 0; |
2185 | 2185 | ||
2186 | if (skb->ip_summed == CHECKSUM_NONE) { | 2186 | th->check = ~csum_fold(th->check + delta); |
2187 | th->check = csum_fold(csum_partial( | 2187 | if (skb->ip_summed != CHECKSUM_HW) |
2188 | skb->h.raw, thlen, csum_add(skb->csum, delta))); | 2188 | th->check = csum_fold(csum_partial(skb->h.raw, thlen, |
2189 | } | 2189 | skb->csum)); |
2190 | 2190 | ||
2191 | seq += len; | 2191 | seq += len; |
2192 | skb = skb->next; | 2192 | skb = skb->next; |
@@ -2196,11 +2196,11 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg) | |||
2196 | th->cwr = 0; | 2196 | th->cwr = 0; |
2197 | } while (skb->next); | 2197 | } while (skb->next); |
2198 | 2198 | ||
2199 | if (skb->ip_summed == CHECKSUM_NONE) { | 2199 | delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len); |
2200 | delta = csum_add(oldlen, htonl(skb->tail - skb->h.raw)); | 2200 | th->check = ~csum_fold(th->check + delta); |
2201 | th->check = csum_fold(csum_partial( | 2201 | if (skb->ip_summed != CHECKSUM_HW) |
2202 | skb->h.raw, thlen, csum_add(skb->csum, delta))); | 2202 | th->check = csum_fold(csum_partial(skb->h.raw, thlen, |
2203 | } | 2203 | skb->csum)); |
2204 | 2204 | ||
2205 | out: | 2205 | out: |
2206 | return segs; | 2206 | return segs; |