aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-04-09 14:59:07 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-04-26 01:28:40 -0400
commit663ead3bb8d5b561e70fc3bb3861c9220b5a77eb (patch)
tree551d031fc9c904487ced4d8d3363e5ed8f369794
parentac758e3c55c529714354fc268892ca4d23ca1e99 (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.h9
-rw-r--r--net/core/dev.c25
-rw-r--r--net/core/skbuff.c2
-rw-r--r--net/ipv4/tcp_ipv4.c2
-rw-r--r--net/ipv4/udp.c1
-rw-r--r--net/ipv6/tcp_ipv6.c2
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);
1155int skb_checksum_help(struct sk_buff *skb) 1155int 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);
1183out_set_summed: 1184out_set_summed:
1184 skb->ip_summed = CHECKSUM_NONE; 1185 skb->ip_summed = CHECKSUM_NONE;
1185out: 1186out:
@@ -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
1441gso: 1446gso:
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;