Local Checksum Offload
Subsystems (7)
v4 cover Local Checksum Offload
Tested with VxLAN, GRE and FOU-IPIP tunnels. Not tested with GENEVE,
because iproute2 doesn't support enabling checksums on GENEVE tunnels.
Also tested VxLAN with IPv6 (as both inner and outer protocol).
Changes from v3:
* Fixed inverted checksum values introduced in v3.
* Don't mangle zero checksums in GRE.
* Clear skb->encapsulation in iptunnel_handle_offloads when not using CHECKSUM_PARTIAL, lest drivers incorrectly interpret that as a request for inner checksum offload.
Changes from v2:
* Added support for IPv4 GRE.
* Split out 'always set up for checksum offload' into its own patch.
* Removed csum_help from iptunnel_handle_offloads.
* Rewrote LCO callers to only fold once.
* Simplified nocheck handling.
Changes from v1:
* Enabled support in more encapsulation protocols.
I think it now covers everything except GRE.
* Wrote up some documentation covering TX checksum offload, LCO and RCO.
Edward Cree (8):
net: local checksum offload for encapsulation
net: udp: always set up for CHECKSUM_PARTIAL offload
net: enable LCO for udp_tunnel_handle_offloads() users
net: vxlan: enable local checksum offload
fou: enable LCO in FOU and GUE
net: gre: Implement LCO for GRE over IPv4
net: ip_tunnel: remove 'csum_help' argument to
iptunnel_handle_offloads
Documentation/networking: add checksum-offloads.txt to explain LCO
Documentation/networking/00-INDEX | 2 + Documentation/networking/checksum-offloads.txt | 119 +++++++++++++++++++++++++ drivers/net/vxlan.c | 18 ++-- include/linux/skbuff.h | 26 ++++++ include/net/ip_tunnels.h | 3 +- include/net/udp_tunnel.h | 2 +- net/ipv4/fou.c | 14 ++- net/ipv4/ip_gre.c | 17 +++- net/ipv4/ip_tunnel_core.c | 22 ++--- net/ipv4/ipip.c | 2 +- net/ipv4/udp.c | 28 ++---- net/ipv6/ip6_checksum.c | 23 ++--- net/ipv6/sit.c | 4 +- net/netfilter/ipvs/ip_vs_xmit.c | 6 +- 14 files changed, 201 insertions(+), 85 deletions(-) create mode 100644 Documentation/networking/checksum-offloads.txt
PATCH v4 1/8 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.
--- include/linux/skbuff.h | 24 ++++++++++++++++++++++++ net/ipv4/ip_tunnel_core.c | 10 +++++----- net/ipv4/udp.c | 20 ++++++++++---------- net/ipv6/ip6_checksum.c | 14 +++++++------- 4 files changed, 46 insertions(+), 22 deletions(-)
include/linux/skbuff.h +24 -0
@@ -3683,5 +3683,29 @@ static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)return hdr_len +skb_gso_transport_seglen (skb); } +/* Local Checksum Offload. +* Compute outer checksum based on the assumption that the +* inner checksum will be offloaded later. +* Fill in outer checksum adjustment (e.g. with sum of outer +* pseudo-header) before calling. +* Also ensure that inner checksum is in linear data area. +*/ +static inline __wsum lco_csum (struct sk_buff *skb) +{ +char *inner_csum_field; +__wsum csum; + +/* Start with complement of inner checksum adjustment */ + inner_csum_field = skb->data +skb_checksum_start_offset (skb) + + skb->csum_offset; + csum = ~csum_unfold (*(__force __sum16 *)inner_csum_field); +/* Add in checksum of our headers (incl. outer checksum +* adjustment filled in by caller) +*/ + csum =skb_checksum (skb,0 ,skb_checksum_start_offset (skb), csum); +/* The result is the checksum from skb->data to end of packet */ +return csum; +} +#endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */
net/ipv4/ip_tunnel_core.c +5 -5
@@ -166,20 +166,20 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,return skb; } -/* If packet is not gso and we are resolving any partial checksum, +/* If packet is not gso and we are not offloading inner checksum, * clear encapsulation flag. This allows setting CHECKSUM_PARTIAL * on the outer header without confusing devices that implement * NETIF_F_IP_CSUM with encapsulation. */ -if (csum_help) - skb->encapsulation =0 ; -if (skb->ip_summed ==CHECKSUM_PARTIAL && csum_help) { + skb->encapsulation =0 ; err =skb_checksum_help (skb);if (unlikely (err)) goto error; - }else if (skb->ip_summed !=CHECKSUM_PARTIAL ) + }else if (skb->ip_summed !=CHECKSUM_PARTIAL ) { skb->ip_summed =CHECKSUM_NONE ; + skb->encapsulation =0 ; + }return skb; error:
net/ipv4/udp.c +10 -10
@@ -848,16 +848,18 @@ void 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 if (skb_dst (skb) &&skb_dst (skb)->dev && - (skb_dst (skb)->dev->features & - (NETIF_F_IP_CSUM |NETIF_F_HW_CSUM ))) { - -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 if (skb_dst (skb) &&skb_dst (skb)->dev && + (skb_dst (skb)->dev->features & + (NETIF_F_IP_CSUM |NETIF_F_HW_CSUM ))) { skb->ip_summed =CHECKSUM_PARTIAL ; skb->csum_start =skb_transport_header (skb) - skb->head; skb->csum_offset = offsetof(struct udphdr , check);
@@ -865,8 +867,6 @@ void udp_set_csum(bool nocheck, struct sk_buff *skb, }else {__wsum csum; -BUG_ON (skb->ip_summed ==CHECKSUM_PARTIAL ); - uh->check =0 ; csum =skb_checksum (skb,0 , len,0 ); uh->check =udp_v4_check (len, saddr, daddr, csum);
net/ipv6/ip6_checksum.c +7 -7
@@ -98,11 +98,13 @@ 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 if (skb_dst(skb) && skb_dst(skb)->dev && - (skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { - -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 if (skb_dst(skb) && skb_dst(skb)->dev && + (skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { skb->ip_summed =CHECKSUM_PARTIAL ; skb->csum_start =skb_transport_header (skb) - skb->head; skb->csum_offset = offsetof(struct udphdr , check);
@@ -110,8 +112,6 @@ void udp6_set_csum(bool nocheck, struct sk_buff *skb, }else {__wsum csum; -BUG_ON (skb->ip_summed ==CHECKSUM_PARTIAL ); - uh->check =0 ; csum =skb_checksum (skb,0 , len,0 ); uh->check =udp_v6_check (len, saddr, daddr, csum);
PATCH v4 2/8 net: udp: always set up for CHECKSUM_PARTIAL offload
In reply to Edward CreeIf the dst device doesn't support it, it'll get fixed up later anyway
by validate_xmit_skb(). Also, this allows us to take advantage of LCO to avoid summing the payload multiple times.
--- net/ipv4/udp.c | 14 +------------- net/ipv6/ip6_checksum.c | 13 +------------ 2 files changed, 2 insertions(+), 25 deletions(-)
net/ipv4/udp.c +1 -13
@@ -857,23 +857,11 @@ void udp_set_csum(bool nocheck, struct sk_buff *skb, uh->check =udp_v4_check (len, saddr, daddr,lco_csum (skb));if (uh->check ==0 ) uh->check =CSUM_MANGLED_0 ; - }else if (skb_dst (skb) &&skb_dst (skb)->dev && - (skb_dst (skb)->dev->features & - (NETIF_F_IP_CSUM |NETIF_F_HW_CSUM ))) { + }else { skb->ip_summed =CHECKSUM_PARTIAL ; skb->csum_start =skb_transport_header (skb) - skb->head; skb->csum_offset = offsetof(struct udphdr , check); uh->check = ~udp_v4_check (len, saddr, daddr,0 ); - }else { -__wsum csum; - - uh->check =0 ; - csum =skb_checksum (skb,0 , len,0 ); - uh->check =udp_v4_check (len, saddr, daddr, csum); -if (uh->check ==0 ) - uh->check =CSUM_MANGLED_0 ; - - skb->ip_summed =CHECKSUM_UNNECESSARY ; } }EXPORT_SYMBOL (udp_set_csum);
net/ipv6/ip6_checksum.c +1 -12
@@ -103,22 +103,11 @@ void udp6_set_csum(bool nocheck, struct sk_buff *skb, uh->check =udp_v6_check (len, saddr, daddr,lco_csum (skb));if (uh->check ==0 ) uh->check =CSUM_MANGLED_0 ; - }else if (skb_dst (skb) &&skb_dst (skb)->dev && - (skb_dst (skb)->dev->features &NETIF_F_IPV6_CSUM )) { + }else { skb->ip_summed =CHECKSUM_PARTIAL ; skb->csum_start =skb_transport_header (skb) - skb->head; skb->csum_offset = offsetof(struct udphdr , check); uh->check = ~udp_v6_check (len, saddr, daddr,0 ); - }else { -__wsum csum; - - uh->check =0 ; - csum =skb_checksum (skb,0 , len,0 ); - uh->check =udp_v6_check (len, saddr, daddr, csum); -if (uh->check ==0 ) - uh->check =CSUM_MANGLED_0 ; - - skb->ip_summed =CHECKSUM_UNNECESSARY ; } }EXPORT_SYMBOL (udp6_set_csum);
PATCH v4 3/8 net: enable LCO for udp_tunnel_handle_offloads() users
In reply to Edward CreeThe only protocol affected at present is Geneve.
--- include/net/udp_tunnel.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
include/net/udp_tunnel.h +2 -1
@@ -103,7 +103,8 @@ static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb, {int type = udp_csum ?SKB_GSO_UDP_TUNNEL_CSUM :SKB_GSO_UDP_TUNNEL ; -return iptunnel_handle_offloads (skb, udp_csum, type); +/* As we're a UDP tunnel, we support LCO, so don't need csum_help */ +return iptunnel_handle_offloads (skb, false, type); }static inline void udp_tunnel_gro_complete (struct sk_buff *skb,int nhoff)
PATCH v4 4/8 net: vxlan: enable local checksum offload
In reply to Edward Cree--- drivers/net/vxlan.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-)
drivers/net/vxlan.c +6 -12
@@ -1706,10 +1706,8 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,if (csum_start <=VXLAN_MAX_REMCSUM_START && !(csum_start &VXLAN_RCO_SHIFT_MASK ) && (skb->csum_offset == offsetof(struct udphdr , check) || - skb->csum_offset == offsetof(struct tcphdr , check))) { - udp_sum = false; + skb->csum_offset == offsetof(struct tcphdr , check))) type |=SKB_GSO_TUNNEL_REMCSUM ; - } }skb_scrub_packet (skb, xnet);
@@ -1731,7 +1729,7 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk, goto err; } - skb =iptunnel_handle_offloads (skb, udp_sum, type); + skb =iptunnel_handle_offloads (skb, false, type);if (IS_ERR (skb)) { err = -EINVAL ; goto err;
@@ -1763,8 +1761,7 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,skb_set_inner_protocol (skb,htons (ETH_P_TEB ));udp_tunnel6_xmit_skb (dst, sk, skb, dev, saddr, daddr, prio, - ttl, src_port, dst_port, - !!(vxflags &VXLAN_F_UDP_ZERO_CSUM6_TX )); + ttl, src_port, dst_port, !udp_sum);return 0 ; err:dst_release (dst);
@@ -1791,10 +1788,8 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skif (csum_start <=VXLAN_MAX_REMCSUM_START && !(csum_start &VXLAN_RCO_SHIFT_MASK ) && (skb->csum_offset == offsetof(struct udphdr , check) || - skb->csum_offset == offsetof(struct tcphdr , check))) { - udp_sum = false; + skb->csum_offset == offsetof(struct tcphdr , check))) type |=SKB_GSO_TUNNEL_REMCSUM ; - } } min_headroom =LL_RESERVED_SPACE (rt->dst.dev) + rt->dst.header_len
@@ -1812,7 +1807,7 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skif (WARN_ON (!skb))return -ENOMEM ; - skb =iptunnel_handle_offloads (skb, udp_sum, type); + skb =iptunnel_handle_offloads (skb, false, type);if (IS_ERR (skb))return PTR_ERR (skb);
@@ -1842,8 +1837,7 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skskb_set_inner_protocol (skb,htons (ETH_P_TEB ));udp_tunnel_xmit_skb (rt, sk, skb, src, dst, tos, ttl, df, - src_port, dst_port, xnet, - !(vxflags &VXLAN_F_UDP_CSUM )); + src_port, dst_port, xnet, !udp_sum);return 0 ; }
PATCH v4 5/8 fou: enable LCO in FOU and GUE
In reply to Edward Cree--- net/ipv4/fou.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
net/ipv4/fou.c +6 -8
@@ -774,7 +774,6 @@ static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, uh->dest = e->dport; uh->source = sport; uh->len =htons (skb->len); - uh->check =0 ;udp_set_csum (!(e->flags &TUNNEL_ENCAP_FLAG_CSUM ), skb, fl4->saddr, fl4->daddr, skb->len);
@@ -784,11 +783,11 @@ static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,int fou_build_header (struct sk_buff *skb,struct ip_tunnel_encap *e,u8 *protocol,struct flowi4 *fl4) { -bool csum = !!(e->flags &TUNNEL_ENCAP_FLAG_CSUM ); -int type = csum ?SKB_GSO_UDP_TUNNEL_CSUM :SKB_GSO_UDP_TUNNEL ; +int type = e->flags &TUNNEL_ENCAP_FLAG_CSUM ?SKB_GSO_UDP_TUNNEL_CSUM : +SKB_GSO_UDP_TUNNEL ;__be16 sport; - skb =iptunnel_handle_offloads (skb, csum, type); + skb =iptunnel_handle_offloads (skb, false, type);if (IS_ERR (skb))return PTR_ERR (skb);
@@ -804,8 +803,8 @@ EXPORT_SYMBOL(fou_build_header);int gue_build_header (struct sk_buff *skb,struct ip_tunnel_encap *e,u8 *protocol,struct flowi4 *fl4) { -bool csum = !!(e->flags &TUNNEL_ENCAP_FLAG_CSUM ); -int type = csum ?SKB_GSO_UDP_TUNNEL_CSUM :SKB_GSO_UDP_TUNNEL ; +int type = e->flags &TUNNEL_ENCAP_FLAG_CSUM ?SKB_GSO_UDP_TUNNEL_CSUM : +SKB_GSO_UDP_TUNNEL ;struct guehdr *guehdr;size_t hdrlen, optlen =0 ;__be16 sport;
@@ -814,7 +813,6 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,if ((e->flags &TUNNEL_ENCAP_FLAG_REMCSUM ) && skb->ip_summed ==CHECKSUM_PARTIAL ) { - csum = false; optlen +=GUE_PLEN_REMCSUM ; type |=SKB_GSO_TUNNEL_REMCSUM ; need_priv = true;
@@ -822,7 +820,7 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, optlen += need_priv ?GUE_LEN_PRIV :0 ; - skb =iptunnel_handle_offloads (skb, csum, type); + skb =iptunnel_handle_offloads (skb, false, type);if (IS_ERR (skb))return PTR_ERR (skb);
PATCH v4 6/8 net: gre: Implement LCO for GRE over IPv4
In reply to Edward Cree--- net/ipv4/ip_gre.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
net/ipv4/ip_gre.c +13 -3
@@ -440,6 +440,17 @@ drop:return 0 ; } +static __sum16 gre_checksum (struct sk_buff *skb) +{ +__wsum csum; + +if (skb->ip_summed ==CHECKSUM_PARTIAL ) + csum =lco_csum (skb); +else + csum =skb_checksum (skb,0 , skb->len,0 ); +return csum_fold (csum); +} +static void build_header (struct sk_buff *skb,int hdr_len,__be16 flags,__be16 proto,__be32 key,__be32 seq) {
@@ -467,8 +478,7 @@ static void build_header(struct sk_buff *skb, int hdr_len, __be16 flags, !(skb_shinfo (skb)->gso_type & (SKB_GSO_GRE | SKB_GSO_GRE_CSUM))) { *ptr =0 ; - *(__sum16 *)ptr =csum_fold (skb_checksum (skb,0 , - skb->len,0 )); + *(__sum16 *)ptr =gre_checksum (skb); } } }
@@ -493,7 +503,7 @@ static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,static struct sk_buff *gre_handle_offloads (struct sk_buff *skb,bool csum) { -return iptunnel_handle_offloads (skb, csum, +return iptunnel_handle_offloads (skb, false, csum ?SKB_GSO_GRE_CSUM :SKB_GSO_GRE ); }
PATCH v4 7/8 net: ip_tunnel: remove 'csum_help' argument to iptunnel_handle_offloads
In reply to Edward CreeAll users now pass false, so we can remove it, and remove the code that
was conditional upon it.
--- drivers/net/vxlan.c | 4 ++-- include/net/ip_tunnels.h | 3 +-- include/net/udp_tunnel.h | 3 +-- net/ipv4/fou.c | 4 ++-- net/ipv4/ip_gre.c | 3 +-- net/ipv4/ip_tunnel_core.c | 18 ++++++------------ net/ipv4/ipip.c | 2 +- net/ipv6/sit.c | 4 ++-- net/netfilter/ipvs/ip_vs_xmit.c | 6 ++---- 9 files changed, 18 insertions(+), 29 deletions(-)
drivers/net/vxlan.c +2 -2
@@ -1729,7 +1729,7 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk, goto err; } - skb =iptunnel_handle_offloads (skb, false, type); + skb =iptunnel_handle_offloads (skb, type);if (IS_ERR (skb)) { err = -EINVAL ; goto err;
@@ -1807,7 +1807,7 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skif (WARN_ON (!skb))return -ENOMEM ; - skb =iptunnel_handle_offloads (skb, false, type); + skb =iptunnel_handle_offloads (skb, type);if (IS_ERR (skb))return PTR_ERR (skb);
include/net/ip_tunnels.h +1 -2
@@ -279,8 +279,7 @@ void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,struct metadata_dst *iptunnel_metadata_reply (struct metadata_dst *md,gfp_t flags); -struct sk_buff *iptunnel_handle_offloads (struct sk_buff *skb,bool gre_csum, -int gso_type_mask); +struct sk_buff *iptunnel_handle_offloads (struct sk_buff *skb,int gso_type_mask);static inline void iptunnel_xmit_stats (struct net_device *dev,int pkt_len) {
include/net/udp_tunnel.h +1 -2
@@ -103,8 +103,7 @@ static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb, {int type = udp_csum ?SKB_GSO_UDP_TUNNEL_CSUM :SKB_GSO_UDP_TUNNEL ; -/* As we're a UDP tunnel, we support LCO, so don't need csum_help */ -return iptunnel_handle_offloads (skb, false, type); +return iptunnel_handle_offloads (skb, type); }static inline void udp_tunnel_gro_complete (struct sk_buff *skb,int nhoff)
net/ipv4/fou.c +2 -2
@@ -787,7 +787,7 @@ int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,SKB_GSO_UDP_TUNNEL ;__be16 sport; - skb =iptunnel_handle_offloads (skb, false, type); + skb =iptunnel_handle_offloads (skb, type);if (IS_ERR (skb))return PTR_ERR (skb);
@@ -820,7 +820,7 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, optlen += need_priv ?GUE_LEN_PRIV :0 ; - skb =iptunnel_handle_offloads (skb, false, type); + skb =iptunnel_handle_offloads (skb, type);if (IS_ERR (skb))return PTR_ERR (skb);
net/ipv4/ip_gre.c +1 -2
@@ -503,8 +503,7 @@ static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,static struct sk_buff *gre_handle_offloads (struct sk_buff *skb,bool csum) { -return iptunnel_handle_offloads (skb, false, - csum ?SKB_GSO_GRE_CSUM :SKB_GSO_GRE ); +return iptunnel_handle_offloads (skb, csum ?SKB_GSO_GRE_CSUM :SKB_GSO_GRE ); } static struct rtable *gre_get_rt(struct sk_buff *skb,
net/ipv4/ip_tunnel_core.c +6 -12
@@ -148,7 +148,6 @@ struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,EXPORT_SYMBOL_GPL (iptunnel_metadata_reply);struct sk_buff *iptunnel_handle_offloads (struct sk_buff *skb, -bool csum_help,int gso_type_mask) {int err;
@@ -166,18 +165,13 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,return skb; } -/* If packet is not gso and we are not offloading inner checksum, -* clear encapsulation flag. This allows setting CHECKSUM_PARTIAL -* on the outer header without confusing devices that implement -* NETIF_F_IP_CSUM with encapsulation. -*/ -if (skb->ip_summed ==CHECKSUM_PARTIAL && csum_help) { - skb->encapsulation =0 ; - 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 ; +/* We clear encapsulation here to prevent badly-written +* drivers potentially deciding to offload an inner checksum +* if we set CHECKSUM_PARTIAL on the outer header. +* This should go away when the drivers are all fixed. +*/ skb->encapsulation =0 ; }
net/ipv4/ipip.c +1 -1
@@ -219,7 +219,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)if (unlikely (skb->protocol !=htons (ETH_P_IP ))) goto tx_error; - skb =iptunnel_handle_offloads (skb, false,SKB_GSO_IPIP ); + skb =iptunnel_handle_offloads (skb,SKB_GSO_IPIP );if (IS_ERR (skb)) goto out;
net/ipv6/sit.c +2 -2
@@ -911,7 +911,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, goto tx_error; } - skb =iptunnel_handle_offloads (skb, false,SKB_GSO_SIT ); + skb =iptunnel_handle_offloads (skb,SKB_GSO_SIT );if (IS_ERR (skb)) {ip_rt_put (rt); goto out;
@@ -1000,7 +1000,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)struct ip_tunnel *tunnel =netdev_priv (dev);const struct iphdr *tiph = &tunnel->parms.iph; - skb =iptunnel_handle_offloads (skb, false,SKB_GSO_IPIP ); + skb =iptunnel_handle_offloads (skb,SKB_GSO_IPIP );if (IS_ERR (skb)) goto out;
net/netfilter/ipvs/ip_vs_xmit.c +2 -4
@@ -1019,8 +1019,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,if (IS_ERR (skb)) goto tx_error; - skb =iptunnel_handle_offloads ( - skb, false,__tun_gso_type_mask (AF_INET , cp->af)); + skb =iptunnel_handle_offloads (skb,__tun_gso_type_mask (AF_INET , cp->af));if (IS_ERR (skb)) goto tx_error;
@@ -1112,8 +1111,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,if (IS_ERR (skb)) goto tx_error; - skb =iptunnel_handle_offloads ( - skb, false,__tun_gso_type_mask (AF_INET6 , cp->af)); + skb =iptunnel_handle_offloads (skb,__tun_gso_type_mask (AF_INET6 , cp->af));if (IS_ERR (skb)) goto tx_error;
PATCH v4 8/8 Documentation/networking: add checksum-offloads.txt to explain LCO
In reply to Edward Cree--- Documentation/networking/00-INDEX | 2 + Documentation/networking/checksum-offloads.txt | 119 +++++++++++++++++++++++++ include/linux/skbuff.h | 2 + 3 files changed, 123 insertions(+) create mode 100644 Documentation/networking/checksum-offloads.txt
Documentation/networking/00-INDEX +2 -0
@@ -44,6 +44,8 @@ can.txt - documentation on CAN protocol family. cdc_mbim.txt - 3G/LTE USB modem (Mobile Broadband Interface Model) +checksum-offloads.txt + - Explanation of checksum offloads; LCO, RCO cops.txt - info on the COPS LocalTalk Linux driver cs89x0.txt
Documentation/networking/checksum-offloads.txt new file +119 -0
119 lines not on this page · raw message
include/linux/skbuff.h +2 -0
@@ -3686,6 +3686,8 @@ static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)/* Local Checksum Offload. * Compute outer checksum based on the assumption that the * inner checksum will be offloaded later. +* See Documentation/networking/checksum-offloads.txt for +* explanation of how this works. * Fill in outer checksum adjustment (e.g. with sum of outer * pseudo-header) before calling. * Also ensure that inner checksum is in linear data area.
David Miller
maintainerFrom: Edward Cree <[email protected]>
Date: Fri, 5 Feb 2016 20:39:34 +0000
Quoted 3 lines
Tested with VxLAN, GRE and FOU-IPIP tunnels. Not tested with GENEVE,
because iproute2 doesn't support enabling checksums on GENEVE tunnels.
Also tested VxLAN with IPv6 (as both inner and outer protocol).
This gets rejects when I try to apply it, please respin.
Thanks!