[ovs-dev] [PATCH] datapath: Handle packets with precomputed checksums.
Jesse Gross
jesse at nicira.com
Sat Jan 23 01:12:39 UTC 2010
Some devices are capable of computing checksums of received packets
in hardware (the GRE module also falls into this category).
Unfortunately before CHECKSUM_HW was split into two different values
it was impossible to tell the checksum status of a packet during
bridging. It turns out that a workaround that we already had in the
GRE module is very similar to that in mainline kernel, so this
generalizes that workaround.
---
datapath/actions.c | 1 +
datapath/datapath.c | 21 +++++++++++++++++++++
datapath/datapath.h | 1 +
datapath/linux-2.6/compat-2.6/ip_gre.c | 7 -------
4 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/datapath/actions.c b/datapath/actions.c
index cadab05..a24abca 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -302,6 +302,7 @@ int dp_xmit_skb(struct sk_buff *skb)
return -E2BIG;
}
+ forward_ip_summed(skb);
dev_queue_xmit(skb);
return len;
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 2a8fb50..5b5cc2c 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -625,6 +625,25 @@ out:
int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
#endif /* CONFIG_XEN && linux == 2.6.18 */
+/* This function serves the same purpose as skb_forward_csum, which exists
+ * in later kernels. However, it is not exactly the same because it only
+ * has an effect on kernels before CHECKSUM_HW was split. Since we are
+ * only concerned with bridging and not other types of forwarding we don't
+ * have to worry about updating checksums which have been already been
+ * computed. The issue that we have is that given an skb with CHECKSUM_HW
+ * we can't tell whether the packet actually has a correct checksum (since
+ * it depends on the whether it is on the transmit or receive path). If we
+ * guess wrong it causes panics, so we just throw away the checksum to be
+ * sure (it isn't common to have a checksum precomputed anyways). */
+void
+forward_ip_summed(struct sk_buff *skb)
+{
+#ifdef CHECKSUM_HW
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+#endif
+}
+
/* Append each packet in 'skb' list to 'queue'. There will be only one packet
* unless we broke up a GSO packet. */
static int
@@ -722,6 +741,8 @@ dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN)
goto err_kfree_skb;
+ forward_ip_summed(skb);
+
/* Break apart GSO packets into their component pieces. Otherwise
* userspace may try to stuff a 64kB packet into a 1500-byte MTU. */
if (skb_is_gso(skb)) {
diff --git a/datapath/datapath.h b/datapath/datapath.h
index 9b4c438..7548ba2 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -169,5 +169,6 @@ static inline int skb_checksum_setup(struct sk_buff *skb)
#endif
int vswitch_skb_checksum_setup(struct sk_buff *skb);
+void forward_ip_summed(struct sk_buff *skb);
#endif /* datapath.h */
diff --git a/datapath/linux-2.6/compat-2.6/ip_gre.c b/datapath/linux-2.6/compat-2.6/ip_gre.c
index 48d7d6c..05c6055 100644
--- a/datapath/linux-2.6/compat-2.6/ip_gre.c
+++ b/datapath/linux-2.6/compat-2.6/ip_gre.c
@@ -679,13 +679,6 @@ static int ipgre_rcv(struct sk_buff *skb)
skb_reset_network_header(skb);
ipgre_ecn_decapsulate(iph, skb);
-#ifdef CHECKSUM_HW
- /* XXX: Temporary workaround to avoid a panic when doing
- * bridging due to multiple meanings of CHECKSUM_HW. */
- if (skb->ip_summed == CHECKSUM_HW)
- skb->ip_summed = CHECKSUM_NONE;
-#endif
-
netif_rx(skb);
read_unlock(&ipgre_lock);
return(0);
--
1.6.3.3
More information about the dev
mailing list