From d0b1c78480ad95436631332a469e65650d2ac71f Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 16 Apr 2009 15:10:26 -0700 Subject: [PATCH] datapath: Fix VLAN tag insertion actions on Xen. 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 | 15 +++++++++++---- datapath/datapath.c | 2 +- datapath/datapath.h | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index 17527931..86892e8d 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -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) diff --git a/datapath/datapath.c b/datapath/datapath.c index 8b26d5a0..eb2c7db0 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -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)) diff --git a/datapath/datapath.h b/datapath/datapath.h index 886296c2..608e7437 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -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 */ -- 2.30.2