Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ip_gre: add csum offload support for gre header

This patch is to add csum offload support for gre header:

On the TX path in gre_build_header(), when CHECKSUM_PARTIAL's set
for inner proto, it will calculate the csum for outer proto, and
inner csum will be offloaded later. Otherwise, CHECKSUM_PARTIAL
and csum_start/offset will be set for outer proto, and the outer
csum will be offloaded later.

On the GSO path in gre_gso_segment(), when CHECKSUM_PARTIAL is
not set for inner proto and the hardware supports csum offload,
CHECKSUM_PARTIAL and csum_start/offset will be set for outer
proto, and outer csum will be offloaded later. Otherwise, it
will do csum for outer proto by calling gso_make_checksum().

Note that SCTP has to do the csum by itself for non GSO path in
sctp_packet_pack(), as gre_build_header() can't handle the csum
with CHECKSUM_PARTIAL set for SCTP CRC csum offload.

v1->v2:
- remove the SCTP part, as GRE dev doesn't support SCTP CRC CSUM
and it will always do checksum for SCTP in sctp_packet_pack()
when it's not a GSO packet.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Xin Long and committed by
Jakub Kicinski
efa1a65c 62fafcd6

+20 -14
+7 -12
include/net/gre.h
··· 106 106 return flags; 107 107 } 108 108 109 - static inline __sum16 gre_checksum(struct sk_buff *skb) 110 - { 111 - __wsum csum; 112 - 113 - if (skb->ip_summed == CHECKSUM_PARTIAL) 114 - csum = lco_csum(skb); 115 - else 116 - csum = skb_checksum(skb, 0, skb->len, 0); 117 - return csum_fold(csum); 118 - } 119 - 120 109 static inline void gre_build_header(struct sk_buff *skb, int hdr_len, 121 110 __be16 flags, __be16 proto, 122 111 __be32 key, __be32 seq) ··· 135 146 !(skb_shinfo(skb)->gso_type & 136 147 (SKB_GSO_GRE | SKB_GSO_GRE_CSUM))) { 137 148 *ptr = 0; 138 - *(__sum16 *)ptr = gre_checksum(skb); 149 + if (skb->ip_summed == CHECKSUM_PARTIAL) { 150 + *(__sum16 *)ptr = csum_fold(lco_csum(skb)); 151 + } else { 152 + skb->ip_summed = CHECKSUM_PARTIAL; 153 + skb->csum_start = skb_transport_header(skb) - skb->head; 154 + skb->csum_offset = sizeof(*greh); 155 + } 139 156 } 140 157 } 141 158 }
+13 -2
net/ipv4/gre_offload.c
··· 15 15 netdev_features_t features) 16 16 { 17 17 int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); 18 + bool need_csum, offload_csum, gso_partial, need_ipsec; 18 19 struct sk_buff *segs = ERR_PTR(-EINVAL); 19 20 u16 mac_offset = skb->mac_header; 20 21 __be16 protocol = skb->protocol; 21 - bool need_csum, gso_partial; 22 22 u16 mac_len = skb->mac_len; 23 23 int gre_offset, outer_hlen; 24 24 ··· 46 46 features &= skb->dev->hw_enc_features; 47 47 if (need_csum) 48 48 features &= ~NETIF_F_SCTP_CRC; 49 + 50 + need_ipsec = skb_dst(skb) && dst_xfrm(skb_dst(skb)); 51 + /* Try to offload checksum if possible */ 52 + offload_csum = !!(need_csum && !need_ipsec && 53 + (skb->dev->features & NETIF_F_HW_CSUM)); 49 54 50 55 /* segment inner packet. */ 51 56 segs = skb_mac_gso_segment(skb, features); ··· 105 100 } 106 101 107 102 *(pcsum + 1) = 0; 108 - *pcsum = gso_make_checksum(skb, 0); 103 + if (skb->encapsulation || !offload_csum) { 104 + *pcsum = gso_make_checksum(skb, 0); 105 + } else { 106 + skb->ip_summed = CHECKSUM_PARTIAL; 107 + skb->csum_start = skb_transport_header(skb) - skb->head; 108 + skb->csum_offset = sizeof(*greh); 109 + } 109 110 } while ((skb = skb->next)); 110 111 out: 111 112 return segs;