[ovs-dev] [PATCH v2] datapath: compat: backport LCO optimization.

Pravin B Shelar pshelar at ovn.org
Wed Aug 17 17:23:01 UTC 2016


This basically backport commit:

    commit 179bc67f69b6cb53ad68cfdec5a917c2a2248355
    Author: Edward Cree <ecree at solarflare.com>
    Date:   Thu Feb 11 20:48:04 2016 +0000

    net: local checksum offload for encapsulation

    The arithmetic properties of the ones-complement checksum mean that a
    correctly checksummed inner packet, including its checksum, has a ones
    complement sum depending only on whatever value was used to initialise
    the checksum field before checksumming (in the case of TCP and UDP,
    this is the ones complement sum of the pseudo header, complemented).
    Consequently, if we are going to offload the inner checksum with
    CHECKSUM_PARTIAL, we can compute the outer checksum based only on the
    packed data not covered by the inner checksum, and the initial value of
    the inner checksum field.

    Signed-off-by: Edward Cree <ecree at solarflare.com>
    Signed-off-by: David S. Miller <davem at davemloft.net>

Signed-off-by: Pravin B Shelar <pshelar at ovn.org>
---
 acinclude.m4                                 |  1 +
 datapath/linux/compat/gso.c                  | 12 ++++++------
 datapath/linux/compat/include/linux/skbuff.h | 23 +++++++++++++++++++++++
 datapath/linux/compat/ip_tunnels_core.c      |  8 +++-----
 datapath/linux/compat/udp.c                  | 14 +++++++++-----
 datapath/linux/compat/udp_tunnel.c           |  9 ++++++---
 6 files changed, 48 insertions(+), 19 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index aa57b47..5ad4857 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -605,6 +605,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_push])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_clear_hash_if_not_l4])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_postpush_rcsum])
+  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [lco_csum])
 
   OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [bool],
                   [OVS_DEFINE([HAVE_BOOL_TYPE])])
diff --git a/datapath/linux/compat/gso.c b/datapath/linux/compat/gso.c
index 89df07f..48a56b9 100644
--- a/datapath/linux/compat/gso.c
+++ b/datapath/linux/compat/gso.c
@@ -236,12 +236,12 @@ static int output_ip(struct sk_buff *skb)
 
 int rpl_ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-	/* This bit set can confuse some drivers on old kernel. */
-	skb->encapsulation = 0;
-
 	if (!OVS_GSO_CB(skb)->fix_segment)
 		return output_ip(skb);
 
+	/* This bit set can confuse some drivers on old kernel. */
+	skb->encapsulation = 0;
+
 	if (skb_is_gso(skb)) {
 		int ret;
 		int id;
@@ -282,12 +282,12 @@ static int output_ipv6(struct sk_buff *skb)
 
 int rpl_ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-	/* This bit set can confuse some drivers on old kernel. */
-	skb->encapsulation = 0;
-
 	if (!OVS_GSO_CB(skb)->fix_segment)
 		return output_ipv6(skb);
 
+	/* This bit set can confuse some drivers on old kernel. */
+	skb->encapsulation = 0;
+
 	if (skb_is_gso(skb)) {
 		int ret;
 
diff --git a/datapath/linux/compat/include/linux/skbuff.h b/datapath/linux/compat/include/linux/skbuff.h
index 57389b5..a2cbd78 100644
--- a/datapath/linux/compat/include/linux/skbuff.h
+++ b/datapath/linux/compat/include/linux/skbuff.h
@@ -348,4 +348,27 @@ static inline void skb_postpush_rcsum(struct sk_buff *skb,
 }
 #endif
 
+#define skb_checksum_start rpl_skb_checksum_start
+static inline unsigned char *skb_checksum_start(const struct sk_buff *skb)
+{
+	return skb->head + skb->csum_start;
+}
+
+#ifndef HAVE_LCO_CSUM
+static inline __wsum lco_csum(struct sk_buff *skb)
+{
+	unsigned char *csum_start = skb_checksum_start(skb);
+	unsigned char *l4_hdr = skb_transport_header(skb);
+	__wsum partial;
+
+	/* Start with complement of inner checksum adjustment */
+	partial = ~csum_unfold(*(__force __sum16 *)(csum_start +
+				skb->csum_offset));
+
+	/* Add in checksum of our headers (incl. outer checksum
+	 * adjustment filled in by caller) and return result.
+	 */
+	return csum_partial(l4_hdr, csum_start - l4_hdr, partial);
+}
+#endif
 #endif
diff --git a/datapath/linux/compat/ip_tunnels_core.c b/datapath/linux/compat/ip_tunnels_core.c
index d1fe20f..4a444f5 100644
--- a/datapath/linux/compat/ip_tunnels_core.c
+++ b/datapath/linux/compat/ip_tunnels_core.c
@@ -126,12 +126,10 @@ int ovs_iptunnel_handle_offloads(struct sk_buff *skb,
 	if (csum_help)
 		skb->encapsulation = 0;
 
-	if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
-		err = skb_checksum_help(skb);
-		if (unlikely(err))
-			goto error;
-	} else if (skb->ip_summed != CHECKSUM_PARTIAL)
+	if (skb->ip_summed != CHECKSUM_PARTIAL) {
 		skb->ip_summed = CHECKSUM_NONE;
+		skb->encapsulation = 0;
+	}
 
 	return 0;
 error:
diff --git a/datapath/linux/compat/udp.c b/datapath/linux/compat/udp.c
index dbeb307..38bf332 100644
--- a/datapath/linux/compat/udp.c
+++ b/datapath/linux/compat/udp.c
@@ -12,13 +12,17 @@ void rpl_udp_set_csum(bool nocheck, struct sk_buff *skb,
 {
 	struct udphdr *uh = udp_hdr(skb);
 
-	if (nocheck)
+
+	if (nocheck) {
 		uh->check = 0;
-	else if (skb_is_gso(skb))
+	} else if (skb_is_gso(skb)) {
 		uh->check = ~udp_v4_check(len, saddr, daddr, 0);
-	else {
-		BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
-
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		uh->check = 0;
+		uh->check = udp_v4_check(len, saddr, daddr, lco_csum(skb));
+		if (uh->check == 0)
+			uh->check = CSUM_MANGLED_0;
+	} else {
 		skb->ip_summed = CHECKSUM_PARTIAL;
 		skb->csum_start = skb_transport_header(skb) - skb->head;
 		skb->csum_offset = offsetof(struct udphdr, check);
diff --git a/datapath/linux/compat/udp_tunnel.c b/datapath/linux/compat/udp_tunnel.c
index fd8e69f..23801bb 100644
--- a/datapath/linux/compat/udp_tunnel.c
+++ b/datapath/linux/compat/udp_tunnel.c
@@ -193,9 +193,12 @@ static void udp6_set_csum(bool nocheck, struct sk_buff *skb,
 		uh->check = 0;
 	else if (skb_is_gso(skb))
 		uh->check = ~udp_v6_check(len, saddr, daddr, 0);
-	else {
-		BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
-
+	else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		uh->check = 0;
+		uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb));
+		if (uh->check == 0)
+			uh->check = CSUM_MANGLED_0;
+	} else {
 		skb->ip_summed = CHECKSUM_PARTIAL;
 		skb->csum_start = skb_transport_header(skb) - skb->head;
 		skb->csum_offset = offsetof(struct udphdr, check);
-- 
2.5.5




More information about the dev mailing list