diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2007-04-09 14:59:07 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:28:40 -0400 |
commit | 663ead3bb8d5b561e70fc3bb3861c9220b5a77eb (patch) | |
tree | 551d031fc9c904487ced4d8d3363e5ed8f369794 | |
parent | ac758e3c55c529714354fc268892ca4d23ca1e99 (diff) |
[NET]: Use csum_start offset instead of skb_transport_header
The skb transport pointer is currently used to specify the start
of the checksum region for transmit checksum offload. Unfortunately,
the same pointer is also used during receive side processing.
This creates a problem when we want to retransmit a received
packet with partial checksums since the skb transport pointer
would be overwritten.
This patch solves this problem by creating a new 16-bit csum_start
offset value to replace the skb transport header for the purpose
of checksums. This offset is calculated from skb->head so that
it does not have to change when skb->data changes.
No extra space is required since csum_offset itself fits within
a 16-bit word so we can use the other 16 bits for csum_start.
For backwards compatibility, just before we push a packet with
partial checksums off into the device driver, we set the skb
transport header to what it would have been under the old scheme.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/skbuff.h | 9 | ||||
-rw-r--r-- | net/core/dev.c | 25 | ||||
-rw-r--r-- | net/core/skbuff.c | 2 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 2 | ||||
-rw-r--r-- | net/ipv4/udp.c | 1 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 2 |
6 files changed, 28 insertions, 13 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 9b2957d203c9..910560e85561 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -205,7 +205,9 @@ typedef unsigned char *sk_buff_data_t; | |||
205 | * @len: Length of actual data | 205 | * @len: Length of actual data |
206 | * @data_len: Data length | 206 | * @data_len: Data length |
207 | * @mac_len: Length of link layer header | 207 | * @mac_len: Length of link layer header |
208 | * @csum: Checksum | 208 | * @csum: Checksum (must include start/offset pair) |
209 | * @csum_start: Offset from skb->head where checksumming should start | ||
210 | * @csum_offset: Offset from csum_start where checksum should be stored | ||
209 | * @local_df: allow local fragmentation | 211 | * @local_df: allow local fragmentation |
210 | * @cloned: Head may be cloned (check refcnt to be sure) | 212 | * @cloned: Head may be cloned (check refcnt to be sure) |
211 | * @nohdr: Payload reference only, must not modify header | 213 | * @nohdr: Payload reference only, must not modify header |
@@ -261,7 +263,10 @@ struct sk_buff { | |||
261 | mac_len; | 263 | mac_len; |
262 | union { | 264 | union { |
263 | __wsum csum; | 265 | __wsum csum; |
264 | __u32 csum_offset; | 266 | struct { |
267 | __u16 csum_start; | ||
268 | __u16 csum_offset; | ||
269 | }; | ||
265 | }; | 270 | }; |
266 | __u32 priority; | 271 | __u32 priority; |
267 | __u8 local_df:1, | 272 | __u8 local_df:1, |
diff --git a/net/core/dev.c b/net/core/dev.c index fec8cf27f75d..d23972f56fc7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1155,7 +1155,7 @@ EXPORT_SYMBOL(netif_device_attach); | |||
1155 | int skb_checksum_help(struct sk_buff *skb) | 1155 | int skb_checksum_help(struct sk_buff *skb) |
1156 | { | 1156 | { |
1157 | __wsum csum; | 1157 | __wsum csum; |
1158 | int ret = 0, offset = skb_transport_offset(skb); | 1158 | int ret = 0, offset; |
1159 | 1159 | ||
1160 | if (skb->ip_summed == CHECKSUM_COMPLETE) | 1160 | if (skb->ip_summed == CHECKSUM_COMPLETE) |
1161 | goto out_set_summed; | 1161 | goto out_set_summed; |
@@ -1171,15 +1171,16 @@ int skb_checksum_help(struct sk_buff *skb) | |||
1171 | goto out; | 1171 | goto out; |
1172 | } | 1172 | } |
1173 | 1173 | ||
1174 | offset = skb->csum_start - skb_headroom(skb); | ||
1174 | BUG_ON(offset > (int)skb->len); | 1175 | BUG_ON(offset > (int)skb->len); |
1175 | csum = skb_checksum(skb, offset, skb->len-offset, 0); | 1176 | csum = skb_checksum(skb, offset, skb->len-offset, 0); |
1176 | 1177 | ||
1177 | offset = skb->tail - skb->transport_header; | 1178 | offset = skb_headlen(skb) - offset; |
1178 | BUG_ON(offset <= 0); | 1179 | BUG_ON(offset <= 0); |
1179 | BUG_ON(skb->csum_offset + 2 > offset); | 1180 | BUG_ON(skb->csum_offset + 2 > offset); |
1180 | 1181 | ||
1181 | *(__sum16 *)(skb_transport_header(skb) + | 1182 | *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = |
1182 | skb->csum_offset) = csum_fold(csum); | 1183 | csum_fold(csum); |
1183 | out_set_summed: | 1184 | out_set_summed: |
1184 | skb->ip_summed = CHECKSUM_NONE; | 1185 | skb->ip_summed = CHECKSUM_NONE; |
1185 | out: | 1186 | out: |
@@ -1431,12 +1432,16 @@ int dev_queue_xmit(struct sk_buff *skb) | |||
1431 | /* If packet is not checksummed and device does not support | 1432 | /* If packet is not checksummed and device does not support |
1432 | * checksumming for this protocol, complete checksumming here. | 1433 | * checksumming for this protocol, complete checksumming here. |
1433 | */ | 1434 | */ |
1434 | if (skb->ip_summed == CHECKSUM_PARTIAL && | 1435 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
1435 | (!(dev->features & NETIF_F_GEN_CSUM) && | 1436 | skb_set_transport_header(skb, skb->csum_start - |
1436 | (!(dev->features & NETIF_F_IP_CSUM) || | 1437 | skb_headroom(skb)); |
1437 | skb->protocol != htons(ETH_P_IP)))) | 1438 | |
1438 | if (skb_checksum_help(skb)) | 1439 | if (!(dev->features & NETIF_F_GEN_CSUM) && |
1439 | goto out_kfree_skb; | 1440 | (!(dev->features & NETIF_F_IP_CSUM) || |
1441 | skb->protocol != htons(ETH_P_IP))) | ||
1442 | if (skb_checksum_help(skb)) | ||
1443 | goto out_kfree_skb; | ||
1444 | } | ||
1440 | 1445 | ||
1441 | gso: | 1446 | gso: |
1442 | spin_lock_prefetch(&dev->queue_lock); | 1447 | spin_lock_prefetch(&dev->queue_lock); |
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4965df29768b..52a4fdd4f31c 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -1358,7 +1358,7 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) | |||
1358 | long csstart; | 1358 | long csstart; |
1359 | 1359 | ||
1360 | if (skb->ip_summed == CHECKSUM_PARTIAL) | 1360 | if (skb->ip_summed == CHECKSUM_PARTIAL) |
1361 | csstart = skb_transport_offset(skb); | 1361 | csstart = skb->csum_start - skb_headroom(skb); |
1362 | else | 1362 | else |
1363 | csstart = skb_headlen(skb); | 1363 | csstart = skb_headlen(skb); |
1364 | 1364 | ||
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index e11eaf4cc269..a091a99ad263 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -504,6 +504,7 @@ void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb) | |||
504 | if (skb->ip_summed == CHECKSUM_PARTIAL) { | 504 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
505 | th->check = ~tcp_v4_check(len, inet->saddr, | 505 | th->check = ~tcp_v4_check(len, inet->saddr, |
506 | inet->daddr, 0); | 506 | inet->daddr, 0); |
507 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
507 | skb->csum_offset = offsetof(struct tcphdr, check); | 508 | skb->csum_offset = offsetof(struct tcphdr, check); |
508 | } else { | 509 | } else { |
509 | th->check = tcp_v4_check(len, inet->saddr, inet->daddr, | 510 | th->check = tcp_v4_check(len, inet->saddr, inet->daddr, |
@@ -526,6 +527,7 @@ int tcp_v4_gso_send_check(struct sk_buff *skb) | |||
526 | 527 | ||
527 | th->check = 0; | 528 | th->check = 0; |
528 | th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0); | 529 | th->check = ~tcp_v4_check(skb->len, iph->saddr, iph->daddr, 0); |
530 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
529 | skb->csum_offset = offsetof(struct tcphdr, check); | 531 | skb->csum_offset = offsetof(struct tcphdr, check); |
530 | skb->ip_summed = CHECKSUM_PARTIAL; | 532 | skb->ip_summed = CHECKSUM_PARTIAL; |
531 | return 0; | 533 | return 0; |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 71b0b60ba538..5ad7a26e3091 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -427,6 +427,7 @@ static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, | |||
427 | /* | 427 | /* |
428 | * Only one fragment on the socket. | 428 | * Only one fragment on the socket. |
429 | */ | 429 | */ |
430 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
430 | skb->csum_offset = offsetof(struct udphdr, check); | 431 | skb->csum_offset = offsetof(struct udphdr, check); |
431 | uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0); | 432 | uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0); |
432 | } else { | 433 | } else { |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 4a55da079f5f..7e824b97126d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -950,6 +950,7 @@ static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) | |||
950 | 950 | ||
951 | if (skb->ip_summed == CHECKSUM_PARTIAL) { | 951 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
952 | th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); | 952 | th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0); |
953 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
953 | skb->csum_offset = offsetof(struct tcphdr, check); | 954 | skb->csum_offset = offsetof(struct tcphdr, check); |
954 | } else { | 955 | } else { |
955 | th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, | 956 | th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, |
@@ -972,6 +973,7 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb) | |||
972 | th->check = 0; | 973 | th->check = 0; |
973 | th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, | 974 | th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, |
974 | IPPROTO_TCP, 0); | 975 | IPPROTO_TCP, 0); |
976 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
975 | skb->csum_offset = offsetof(struct tcphdr, check); | 977 | skb->csum_offset = offsetof(struct tcphdr, check); |
976 | skb->ip_summed = CHECKSUM_PARTIAL; | 978 | skb->ip_summed = CHECKSUM_PARTIAL; |
977 | return 0; | 979 | return 0; |