datapath: Fix VLAN tag insertion actions on Xen.
authorBen Pfaff <blp@nicira.com>
Thu, 16 Apr 2009 22:10:26 +0000 (15:10 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 16 Apr 2009 22:10:26 +0000 (15:10 -0700)
On Xen, a VM can pass a packet that needs to be checksummed up to Dom0 for
transmission on the wire.  In this case, Dom0 is supposed to pick apart
the packet, figure out the protocol, and checksum it before sending it out.
However, this fails if we insert an 802.1Q header, because the Xen routine
that picks apart packets (skb_checksum_setup() in net/core/dev.c) does not
understand 802.1Q.  Hence, we must call this function ourselves, before we
add the 802.1Q header.

Fixes bug #1215.

datapath/actions.c
datapath/datapath.c
datapath/datapath.h

index 17527931e7e6ad7a122a2ec868a442ae962ef0d2..86892e8dc4da253bd036e6620acd66e27e03fe04 100644 (file)
@@ -75,10 +75,17 @@ modify_vlan_tci(struct sk_buff *skb, struct odp_flow_key *key,
        } else  {
                /* Add vlan header */
 
-               /* xxx The vlan_put_tag function, doesn't seem to work
-                * xxx reliably when it attempts to use the hardware-accelerated
-                * xxx version.  We'll directly use the software version
-                * xxx until the problem can be diagnosed.
+               /* Set up checksumming pointers for checksum-deferred packets
+                * on Xen.  Otherwise, dev_queue_xmit() will try to do this
+                * when we send the packet out on the wire, and it will fail at
+                * that point because skb_checksum_setup() will not look inside
+                * an 802.1Q header. */
+               skb_checksum_setup(skb);
+
+               /* The hardware-accelerated version of vlan_put_tag() works
+                * only for a device that has a VLAN group configured (with
+                * e.g. vconfig(8)), so call the software-only version
+                * __vlan_put_tag() directly instead.
                 */
                skb = __vlan_put_tag(skb, tci & mask);
                if (!skb)
index 8b26d5a011f1c5c66b4df6ed6bae5ba261701de0..eb2c7db04c712687077edae10542665b874f303d 100644 (file)
@@ -632,7 +632,7 @@ static int skb_pull_up_to(struct sk_buff *skb, void *ptr)
        }
 }
 
-inline int skb_checksum_setup(struct sk_buff *skb)
+int skb_checksum_setup(struct sk_buff *skb)
 {
        if (skb->proto_csum_blank) {
                if (skb->protocol != htons(ETH_P_IP))
index 886296c26399aedfd68931ce1d59e9f3b1355350..608e74373956a10441f1d2de185d1cb982071f2b 100644 (file)
@@ -117,4 +117,13 @@ void dp_set_origin(struct datapath *, u16, struct sk_buff *);
 /* Should hold at least RCU read lock when calling */
 struct datapath *get_dp(int dp_idx);
 
+#ifdef CONFIG_XEN
+int skb_checksum_setup(struct sk_buff *skb);
+#else
+static inline int skb_checksum_setup(struct sk_buff *skb)
+{
+       return 0;
+}
+#endif
+
 #endif /* datapath.h */