Skip to content

Commit 0e3e466

Browse files
committed
Merge branch 'udp-gso-cleanups'
Alexander Duyck says: ==================== UDP GSO Segmentation clean-ups This patch set addresses a number of issues I found while sorting out enabling UDP GSO Segmentation support for ixgbe/ixgbevf. Specifically there were a number of issues related to the checksum and such that seemed to cause either minor irregularities or kernel panics in the case of the offload request being allowed to traverse between name spaces. With this set applied I am was able to get UDP GSO traffic to pass over vxlan tunnels in both offloaded modes and non-offloaded modes for ixgbe and ixgbevf. I submitted the driver specific patches earlier as an RFC: https://patchwork.ozlabs.org/project/netdev/list/?series=42477&archive=both&state=* v2: Updated patches based on feedback from Eric Dumazet Split first patch into several patches based on feedback from Eric v3: Drop patch that was calling pskb_may_pull as it was redundant. Added code to use MANGLED_0 in case of UDP checksum Drop patch adding NETIF_F_GSO_UDP_L4 to list of GSO software offloads Added Acked-by for patches reviewed by Willem and not changed ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 53a7bdf + 04d55b2 commit 0e3e466

File tree

4 files changed

+64
-51
lines changed

4 files changed

+64
-51
lines changed

include/net/udp.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,7 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
175175
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
176176

177177
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
178-
netdev_features_t features,
179-
unsigned int mss, __sum16 check);
178+
netdev_features_t features);
180179

181180
static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
182181
{

net/ipv4/udp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
793793

794794
skb_shinfo(skb)->gso_size = cork->gso_size;
795795
skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4;
796+
skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(len - sizeof(uh),
797+
cork->gso_size);
796798
goto csum_partial;
797799
}
798800

net/ipv4/udp_offload.c

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -188,66 +188,92 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
188188
EXPORT_SYMBOL(skb_udp_tunnel_segment);
189189

190190
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
191-
netdev_features_t features,
192-
unsigned int mss, __sum16 check)
191+
netdev_features_t features)
193192
{
194193
struct sock *sk = gso_skb->sk;
195194
unsigned int sum_truesize = 0;
196195
struct sk_buff *segs, *seg;
197-
unsigned int hdrlen;
198196
struct udphdr *uh;
197+
unsigned int mss;
198+
bool copy_dtor;
199+
__sum16 check;
200+
__be16 newlen;
199201

202+
mss = skb_shinfo(gso_skb)->gso_size;
200203
if (gso_skb->len <= sizeof(*uh) + mss)
201204
return ERR_PTR(-EINVAL);
202205

203-
hdrlen = gso_skb->data - skb_mac_header(gso_skb);
204206
skb_pull(gso_skb, sizeof(*uh));
205207

206208
/* clear destructor to avoid skb_segment assigning it to tail */
207-
WARN_ON_ONCE(gso_skb->destructor != sock_wfree);
208-
gso_skb->destructor = NULL;
209+
copy_dtor = gso_skb->destructor == sock_wfree;
210+
if (copy_dtor)
211+
gso_skb->destructor = NULL;
209212

210213
segs = skb_segment(gso_skb, features);
211214
if (unlikely(IS_ERR_OR_NULL(segs))) {
212-
gso_skb->destructor = sock_wfree;
215+
if (copy_dtor)
216+
gso_skb->destructor = sock_wfree;
213217
return segs;
214218
}
215219

216-
for (seg = segs; seg; seg = seg->next) {
217-
uh = udp_hdr(seg);
218-
uh->len = htons(seg->len - hdrlen);
219-
uh->check = check;
220+
/* GSO partial and frag_list segmentation only requires splitting
221+
* the frame into an MSS multiple and possibly a remainder, both
222+
* cases return a GSO skb. So update the mss now.
223+
*/
224+
if (skb_is_gso(segs))
225+
mss *= skb_shinfo(segs)->gso_segs;
226+
227+
seg = segs;
228+
uh = udp_hdr(seg);
229+
230+
/* compute checksum adjustment based on old length versus new */
231+
newlen = htons(sizeof(*uh) + mss);
232+
check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
233+
234+
for (;;) {
235+
if (copy_dtor) {
236+
seg->destructor = sock_wfree;
237+
seg->sk = sk;
238+
sum_truesize += seg->truesize;
239+
}
220240

221-
/* last packet can be partial gso_size */
222241
if (!seg->next)
223-
csum_replace2(&uh->check, htons(mss),
224-
htons(seg->len - hdrlen - sizeof(*uh)));
242+
break;
225243

226-
uh->check = ~uh->check;
227-
seg->destructor = sock_wfree;
228-
seg->sk = sk;
229-
sum_truesize += seg->truesize;
230-
}
244+
uh->len = newlen;
245+
uh->check = check;
231246

232-
refcount_add(sum_truesize - gso_skb->truesize, &sk->sk_wmem_alloc);
247+
if (seg->ip_summed == CHECKSUM_PARTIAL)
248+
gso_reset_checksum(seg, ~check);
249+
else
250+
uh->check = gso_make_checksum(seg, ~check) ? :
251+
CSUM_MANGLED_0;
233252

234-
return segs;
235-
}
236-
EXPORT_SYMBOL_GPL(__udp_gso_segment);
253+
seg = seg->next;
254+
uh = udp_hdr(seg);
255+
}
237256

238-
static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb,
239-
netdev_features_t features)
240-
{
241-
const struct iphdr *iph = ip_hdr(gso_skb);
242-
unsigned int mss = skb_shinfo(gso_skb)->gso_size;
257+
/* last packet can be partial gso_size, account for that in checksum */
258+
newlen = htons(skb_tail_pointer(seg) - skb_transport_header(seg) +
259+
seg->data_len);
260+
check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
243261

244-
if (!can_checksum_protocol(features, htons(ETH_P_IP)))
245-
return ERR_PTR(-EIO);
262+
uh->len = newlen;
263+
uh->check = check;
264+
265+
if (seg->ip_summed == CHECKSUM_PARTIAL)
266+
gso_reset_checksum(seg, ~check);
267+
else
268+
uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;
246269

247-
return __udp_gso_segment(gso_skb, features, mss,
248-
udp_v4_check(sizeof(struct udphdr) + mss,
249-
iph->saddr, iph->daddr, 0));
270+
/* update refcount for the packet */
271+
if (copy_dtor)
272+
refcount_add(sum_truesize - gso_skb->truesize,
273+
&sk->sk_wmem_alloc);
274+
return segs;
250275
}
276+
EXPORT_SYMBOL_GPL(__udp_gso_segment);
251277

252278
static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
253279
netdev_features_t features)
@@ -272,7 +298,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
272298
goto out;
273299

274300
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
275-
return __udp4_gso_segment(skb, features);
301+
return __udp_gso_segment(skb, features);
276302

277303
mss = skb_shinfo(skb)->gso_size;
278304
if (unlikely(skb->len <= mss))

net/ipv6/udp_offload.c

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,6 @@
1717
#include <net/ip6_checksum.h>
1818
#include "ip6_offload.h"
1919

20-
static struct sk_buff *__udp6_gso_segment(struct sk_buff *gso_skb,
21-
netdev_features_t features)
22-
{
23-
const struct ipv6hdr *ip6h = ipv6_hdr(gso_skb);
24-
unsigned int mss = skb_shinfo(gso_skb)->gso_size;
25-
26-
if (!can_checksum_protocol(features, htons(ETH_P_IPV6)))
27-
return ERR_PTR(-EIO);
28-
29-
return __udp_gso_segment(gso_skb, features, mss,
30-
udp_v6_check(sizeof(struct udphdr) + mss,
31-
&ip6h->saddr, &ip6h->daddr, 0));
32-
}
33-
3420
static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
3521
netdev_features_t features)
3622
{
@@ -63,7 +49,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
6349
goto out;
6450

6551
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
66-
return __udp6_gso_segment(skb, features);
52+
return __udp_gso_segment(skb, features);
6753

6854
/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
6955
* do checksum of UDP packets sent as multiple IP fragments.

0 commit comments

Comments
 (0)