Skip to content

Commit 4bcb877

Browse files
Tom Herbertdavem330
authored andcommitted
udp: Offload outer UDP tunnel csum if available
In __skb_udp_tunnel_segment if outer UDP checksums are enabled and ip_summed is not already CHECKSUM_PARTIAL, set up checksum offload if device features allow it. Signed-off-by: Tom Herbert <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 63487ba commit 4bcb877

File tree

1 file changed

+36
-16
lines changed

1 file changed

+36
-16
lines changed

net/ipv4/udp_offload.c

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
2929
netdev_features_t features,
3030
struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb,
3131
netdev_features_t features),
32-
__be16 new_protocol)
32+
__be16 new_protocol, bool is_ipv6)
3333
{
3434
struct sk_buff *segs = ERR_PTR(-EINVAL);
3535
u16 mac_offset = skb->mac_header;
@@ -39,7 +39,9 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
3939
netdev_features_t enc_features;
4040
int udp_offset, outer_hlen;
4141
unsigned int oldlen;
42-
bool need_csum;
42+
bool need_csum = !!(skb_shinfo(skb)->gso_type &
43+
SKB_GSO_UDP_TUNNEL_CSUM);
44+
bool offload_csum = false, dont_encap = need_csum;
4345

4446
oldlen = (u16)~skb->len;
4547

@@ -52,10 +54,12 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
5254
skb_set_network_header(skb, skb_inner_network_offset(skb));
5355
skb->mac_len = skb_inner_network_offset(skb);
5456
skb->protocol = new_protocol;
57+
skb->encap_hdr_csum = need_csum;
5558

56-
need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
57-
if (need_csum)
58-
skb->encap_hdr_csum = 1;
59+
/* Try to offload checksum if possible */
60+
offload_csum = !!(need_csum &&
61+
(skb->dev->features &
62+
(is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM)));
5963

6064
/* segment inner packet. */
6165
enc_features = skb->dev->hw_enc_features & features;
@@ -72,11 +76,21 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
7276
do {
7377
struct udphdr *uh;
7478
int len;
75-
76-
skb_reset_inner_headers(skb);
77-
skb->encapsulation = 1;
79+
__be32 delta;
80+
81+
if (dont_encap) {
82+
skb->encapsulation = 0;
83+
skb->ip_summed = CHECKSUM_NONE;
84+
} else {
85+
/* Only set up inner headers if we might be offloading
86+
* inner checksum.
87+
*/
88+
skb_reset_inner_headers(skb);
89+
skb->encapsulation = 1;
90+
}
7891

7992
skb->mac_len = mac_len;
93+
skb->protocol = protocol;
8094

8195
skb_push(skb, outer_hlen);
8296
skb_reset_mac_header(skb);
@@ -86,19 +100,25 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
86100
uh = udp_hdr(skb);
87101
uh->len = htons(len);
88102

89-
if (need_csum) {
90-
__be32 delta = htonl(oldlen + len);
103+
if (!need_csum)
104+
continue;
105+
106+
delta = htonl(oldlen + len);
107+
108+
uh->check = ~csum_fold((__force __wsum)
109+
((__force u32)uh->check +
110+
(__force u32)delta));
91111

92-
uh->check = ~csum_fold((__force __wsum)
93-
((__force u32)uh->check +
94-
(__force u32)delta));
112+
if (offload_csum) {
113+
skb->ip_summed = CHECKSUM_PARTIAL;
114+
skb->csum_start = skb_transport_header(skb) - skb->head;
115+
skb->csum_offset = offsetof(struct udphdr, check);
116+
} else {
95117
uh->check = gso_make_checksum(skb, ~uh->check);
96118

97119
if (uh->check == 0)
98120
uh->check = CSUM_MANGLED_0;
99121
}
100-
101-
skb->protocol = protocol;
102122
} while ((skb = skb->next));
103123
out:
104124
return segs;
@@ -134,7 +154,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
134154
}
135155

136156
segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment,
137-
protocol);
157+
protocol, is_ipv6);
138158

139159
out_unlock:
140160
rcu_read_unlock();

0 commit comments

Comments
 (0)