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 53bf3ef..b059cc0 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 d8205c6..d9f043a 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