aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--datapath/actions.c28
-rw-r--r--datapath/datapath.c55
-rw-r--r--datapath/datapath.h9
3 files changed, 73 insertions, 19 deletions
diff --git a/datapath/actions.c b/datapath/actions.c
index b20873bf..b6d5488c 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -65,11 +65,14 @@ vlan_pull_tag(struct sk_buff *skb)
struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
struct ethhdr *eh;
-
/* Verify we were given a vlan packet */
if (vh->h_vlan_proto != htons(ETH_P_8021Q))
return skb;
+ if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE)
+ skb->csum = csum_sub(skb->csum, csum_partial(skb->data
+ + ETH_HLEN, VLAN_HLEN, 0));
+
memmove(skb->data + VLAN_HLEN, skb->data, 2 * VLAN_ETH_ALEN);
eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
@@ -104,7 +107,16 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb,
if (skb->protocol == htons(ETH_P_8021Q)) {
/* Modify vlan id, but maintain other TCI values */
struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
+ __be16 old_tci = vh->h_vlan_TCI;
+
vh->h_vlan_TCI = htons((ntohs(vh->h_vlan_TCI) & ~mask) | tci);
+
+ if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE) {
+ __be16 diff[] = { ~old_tci, vh->h_vlan_TCI };
+
+ skb->csum = ~csum_partial((char *)diff, sizeof(diff),
+ ~skb->csum);
+ }
} else {
/* Add vlan header */
@@ -144,6 +156,9 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb,
segs->next = NULL;
+ /* GSO can change the checksum type so update.*/
+ compute_ip_summed(segs, true);
+
segs = __vlan_put_tag(segs, tci);
err = -ENOMEM;
if (segs) {
@@ -167,6 +182,7 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb,
} while (segs->next);
skb = segs;
+ compute_ip_summed(skb, true);
}
/* The hardware-accelerated version of vlan_put_tag() works
@@ -177,6 +193,12 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb,
skb = __vlan_put_tag(skb, tci);
if (!skb)
return ERR_PTR(-ENOMEM);
+
+ /* GSO doesn't fix up the hardware computed checksum so this
+ * will only be hit in the non-GSO case. */
+ if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE)
+ skb->csum = csum_add(skb->csum, csum_partial(skb->data
+ + ETH_HLEN, VLAN_HLEN, 0));
}
return skb;
@@ -215,10 +237,10 @@ static void update_csum(__sum16 *sum, struct sk_buff *skb,
{
__be32 diff[] = { ~from, to };
- if (OVS_CB(skb)->ip_summed != CSUM_PARTIAL) {
+ if (OVS_CB(skb)->ip_summed != OVS_CSUM_PARTIAL) {
*sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
~csum_unfold(*sum)));
- if (OVS_CB(skb)->ip_summed == CSUM_COMPLETE && pseudohdr)
+ if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE && pseudohdr)
skb->csum = ~csum_partial((char *)diff, sizeof(diff),
~skb->csum);
} else if (pseudohdr)
diff --git a/datapath/datapath.c b/datapath/datapath.c
index b6aefe89..76569f50 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -71,7 +71,6 @@ static DEFINE_MUTEX(dp_mutex);
#define MAINT_SLEEP_MSECS 1000
static int new_nbp(struct datapath *, struct net_device *, int port_no);
-static void compute_ip_summed(struct sk_buff *skb);
/* Must be called with rcu_read_lock or dp_mutex. */
struct datapath *get_dp(int dp_idx)
@@ -530,7 +529,7 @@ void dp_process_received_packet(struct sk_buff *skb, struct net_bridge_port *p)
WARN_ON_ONCE(skb_shared(skb));
- compute_ip_summed(skb);
+ compute_ip_summed(skb, false);
/* BHs are off so we don't have to use get_cpu()/put_cpu() here. */
stats = percpu_ptr(dp->stats_percpu, smp_processor_id());
@@ -699,25 +698,57 @@ out:
* skb_forward_csum(). It is slightly different because we are only concerned
* with bridging and not other types of forwarding and can get away with
* slightly more optimal behavior.*/
-static void
-compute_ip_summed(struct sk_buff *skb)
+void
+compute_ip_summed(struct sk_buff *skb, bool xmit)
{
- OVS_CB(skb)->ip_summed = skb->ip_summed;
-
+ /* For our convenience these defines change repeatedly between kernel
+ * versions, so we can't just copy them over... */
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ OVS_CB(skb)->ip_summed = OVS_CSUM_NONE;
+ break;
+ case CHECKSUM_UNNECESSARY:
+ OVS_CB(skb)->ip_summed = OVS_CSUM_UNNECESSARY;
+ break;
#ifdef CHECKSUM_HW
/* 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)
- OVS_CB(skb)->ip_summed = CSUM_COMPLETE;
+ * practice.
+ *
+ * The one exception to this is if we are on the transmit path
+ * (basically after skb_checksum_setup() has been run) the type has
+ * already been converted, so we should stay with that. */
+ case CHECKSUM_HW:
+ if (!xmit)
+ OVS_CB(skb)->ip_summed = OVS_CSUM_COMPLETE;
+ else
+ OVS_CB(skb)->ip_summed = OVS_CSUM_PARTIAL;
+
+ break;
+#else
+ case CHECKSUM_COMPLETE:
+ OVS_CB(skb)->ip_summed = OVS_CSUM_COMPLETE;
+ break;
+ case CHECKSUM_PARTIAL:
+ OVS_CB(skb)->ip_summed = OVS_CSUM_PARTIAL;
+ break;
#endif
+ default:
+ printk(KERN_ERR "openvswitch: unknown checksum type %d\n",
+ skb->ip_summed);
+ /* None seems the safest... */
+ OVS_CB(skb)->ip_summed = OVS_CSUM_NONE;
+ }
+
#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
/* Xen has a special way of representing CHECKSUM_PARTIAL on older
- * kernels. */
+ * kernels. It should not be set on the transmit path though. */
if (skb->proto_csum_blank)
- OVS_CB(skb)->ip_summed = CSUM_PARTIAL;
+ OVS_CB(skb)->ip_summed = OVS_CSUM_PARTIAL;
+
+ WARN_ON_ONCE(skb->proto_csum_blank && xmit);
#endif
}
@@ -725,7 +756,7 @@ void
forward_ip_summed(struct sk_buff *skb)
{
#ifdef CHECKSUM_HW
- if (OVS_CB(skb)->ip_summed == CSUM_COMPLETE)
+ if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE)
skb->ip_summed = CHECKSUM_NONE;
#endif
}
diff --git a/datapath/datapath.h b/datapath/datapath.h
index d9fe4b2f..38c84756 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -183,10 +183,10 @@ struct net_bridge_port {
};
enum csum_type {
- CSUM_NONE = 0,
- CSUM_UNNECESSARY = 1,
- CSUM_COMPLETE = 2,
- CSUM_PARTIAL = 3,
+ OVS_CSUM_NONE = 0,
+ OVS_CSUM_UNNECESSARY = 1,
+ OVS_CSUM_COMPLETE = 2,
+ OVS_CSUM_PARTIAL = 3,
};
/**
@@ -236,6 +236,7 @@ static inline int vswitch_skb_checksum_setup(struct sk_buff *skb)
}
#endif
+void compute_ip_summed(struct sk_buff *skb, bool xmit);
void forward_ip_summed(struct sk_buff *skb);
#endif /* datapath.h */