aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/dev.c
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 /net/core/dev.c
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>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r--net/core/dev.c25
1 files changed, 15 insertions, 10 deletions
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);