datapath: Don't compute checksums on partial packets.
authorJesse Gross <jesse@nicira.com>
Sat, 19 Jun 2010 00:11:44 +0000 (17:11 -0700)
committerJesse Gross <jesse@nicira.com>
Sat, 19 Jun 2010 00:11:44 +0000 (17:11 -0700)
If we are only copying part of a packet to userspace don't bother
computing the checksum on that part since it is meaningless.
Instead, fall back to the old method of checksumming before
copying to ensure the correct result.

This was supposed to be part of the previous commit but was left
off.

datapath/datapath.c

index 491f98a59b5f486c7383004a8e4768261c8ed34c..c715f0ec7d3650f879efb720f2d3c176823959a8 100644 (file)
@@ -744,16 +744,6 @@ queue_control_packets(struct sk_buff *skb, struct sk_buff_head *queue,
                nskb = skb->next;
                skb->next = NULL;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-               /* Until 2.6.22, the start of the transport header was
-                * also the start of data to be checksummed.  Linux
-                * 2.6.22 introduced the csum_start field for this
-                * purpose, but we should point the transport header to
-                * it anyway for backward compatibility, as
-                * dev_queue_xmit() does even in 2.6.28. */
-               skb_set_transport_header(skb, skb->csum_start - skb_headroom(skb));
-#endif
-
                err = skb_cow(skb, sizeof *header);
                if (err)
                        goto err_kfree_skbs;
@@ -2201,19 +2191,25 @@ success:
        
        retval = 0;
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               __wsum csum = 0;
-               int csum_start, csum_offset;
+               if (copy_bytes == skb->len) {
+                       __wsum csum = 0;
+                       int csum_start, csum_offset;
 
-               csum_start = skb_transport_header(skb) - skb->data;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-               csum_offset = skb->csum_offset;
+                       /* Until 2.6.22, the start of the transport header was
+                        * also the start of data to be checksummed.  Linux
+                        * 2.6.22 introduced the csum_start field for this
+                        * purpose, but we should point the transport header to
+                        * it anyway for backward compatibility, as
+                        * dev_queue_xmit() does even in 2.6.28. */
+                       skb_set_transport_header(skb, skb->csum_start - skb_headroom(skb));
+                       csum_offset = skb->csum_offset;
 #else
-               csum_offset = skb->csum;
+                       csum_offset = skb->csum;
 #endif
-               if (csum_start + csum_offset + sizeof(__sum16) <= copy_bytes) {
+                       csum_start = skb_transport_header(skb) - skb->data;
                        retval = skb_copy_and_csum_datagram(skb, csum_start, buf + csum_start,
                                                            copy_bytes - csum_start, &csum);
-
                        if (!retval) {
                                __sum16 __user *csump;
 
@@ -2221,7 +2217,8 @@ success:
                                csump = (__sum16 __user *)(buf + csum_start + csum_offset);
                                put_user(csum_fold(csum), csump);
                        }
-               }
+               } else
+                       retval = skb_checksum_help(skb);
        }
 
        if (!retval) {