datapath: Support CHECKSUM_PARTIAL on older kernels.
authorJesse Gross <jesse@nicira.com>
Mon, 25 Jan 2010 00:58:46 +0000 (19:58 -0500)
committerJesse Gross <jesse@nicira.com>
Tue, 26 Jan 2010 23:09:34 +0000 (18:09 -0500)
On older kernels we would not correctly update partial checksums
because it was difficult to determine the type of checksum.  This
uses some hints to infer the correct type of checksum so that it
can be updated.  It also allows us to correctly define
CHECKSUM_PARTIAL, which is important for other components.

datapath/actions.c
datapath/linux-2.6/compat-2.6/include/linux/skbuff.h

index 53bf3efd3e8dce99adcf8622345844ab86fbcc28..b059cc0a22c651e9e8f0bd58c96551370c50e61f 100644 (file)
@@ -213,10 +213,43 @@ static void update_csum(__sum16 *sum, struct sk_buff *skb,
                        __be32 from, __be32 to, int pseudohdr)
 {
        __be32 diff[] = { ~from, to };
-       if (skb->ip_summed != CHECKSUM_PARTIAL) {
+
+/* On older kernels, CHECKSUM_PARTIAL and CHECKSUM_COMPLETE are both defined
+ * as CHECKSUM_HW.  However, we can make some inferences so that we can update
+ * the checksums appropriately. */
+       enum {
+               CSUM_PARTIAL,   /* Partial checksum, skb->csum undefined. */
+               CSUM_PACKET,    /* In-packet checksum, skb->csum undefined. */
+               CSUM_COMPLETE,  /* In-packet checksum, skb->csum valid. */
+       } csum_type;
+
+       csum_type = CSUM_PACKET;
+#ifndef CHECKSUM_HW
+       /* Newer kernel, just map between kernel types and ours. */
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               csum_type = CSUM_PARTIAL;
+       else if (skb->ip_summed == CHECKSUM_COMPLETE)
+               csum_type = CSUM_COMPLETE;
+#else
+       /* In theory this could be either CHECKSUM_PARTIAL or CHECKSUM_COMPLETE.
+        * However, we should only get CHECKSUM_PARTIAL packets from Xen, which
+        * uses some special fields to represent this (see below).  Since we
+        * can only make one type work, pick the one that actually happens in
+        * practice. */
+       if (skb->ip_summed == CHECKSUM_HW)
+               csum_type = CSUM_COMPLETE;
+#endif
+#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
+       /* Xen has a special way of representing CHECKSUM_PARTIAL on older
+        * kernels. */
+       if (skb->proto_csum_blank)
+               csum_type = CSUM_PARTIAL;
+#endif
+
+       if (csum_type != CSUM_PARTIAL) {
                *sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
                                ~csum_unfold(*sum)));
-               if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
+               if (csum_type == CSUM_COMPLETE && pseudohdr)
                        skb->csum = ~csum_partial((char *)diff, sizeof(diff),
                                                ~skb->csum);
        } else if (pseudohdr)
index d8205c6a24408383bee5b8833879e8bfd1197ce8..d9f043ac6442960e8f5841b06dd070b1881a86da 100644 (file)
@@ -113,9 +113,7 @@ static inline void kfree_skb_maybe_null(struct sk_buff *skb)
 
 
 #ifndef CHECKSUM_PARTIAL
-/* Note that CHECKSUM_PARTIAL is not implemented, but this allows us to at
- * least test against it: see update_csum() in forward.c. */
-#define CHECKSUM_PARTIAL 3
+#define CHECKSUM_PARTIAL CHECKSUM_HW
 #endif
 #ifndef CHECKSUM_COMPLETE
 #define CHECKSUM_COMPLETE CHECKSUM_HW