Skip to contentSkip to conversation

Series short description

PATCH In review Alexander Duyck 3 participants 15 messages May 7, 2018

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v3 cover Series short description

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

---

Alexander Duyck (6):
udp: Record gso_segs when supporting UDP segmentation offload
udp: Do not pass MSS as parameter to GSO segmentation
udp: Do not pass checksum as a parameter to GSO segmentation
udp: Partially unroll handling of first segment and last segment
udp: Add support for software checksum and GSO_PARTIAL with GSO offload
udp: Do not copy destructor if one is not present

 include/net/udp.h      |    3 +-
 net/ipv4/udp.c         |    2 +
 net/ipv4/udp_offload.c |   94 +++++++++++++++++++++++++++++++-----------------
 net/ipv6/udp_offload.c |   16 +-------
 4 files changed, 64 insertions(+), 51 deletions(-)
#1 lore raw

PATCH v3 1/6 udp: Record gso_segs when supporting UDP segmentation offload

From: Alexander Duyck <[email protected]>

We need to record the number of segments that will be generated when this frame is segmented. The expectation is that if gso_size is set then gso_segs is set as well. Without this some drivers such as ixgbe get confused if they attempt to offload this as they record 0 segments for the entire packet instead of the correct value.

---
 net/ipv4/udp.c |    2 ++
 1 file changed, 2 insertions(+)
net/ipv4/udp.c +2 -0
index dd3102a37ef9..e07db83b311e 100644
@@ -793,6 +793,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
 
 		skb_shinfo(skb)->gso_size = cork->gso_size;
 		skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4;
+		skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(len - sizeof(uh),
+							 cork->gso_size);
 		goto csum_partial;
 	}
 
#2 lore raw

PATCH v3 2/6 udp: Do not pass MSS as parameter to GSO segmentation

In reply to Alexander Duyck

From: Alexander Duyck <[email protected]>

There is no point in passing MSS as a parameter for for the GSO segmentation call as it is already available via the shared info for the skb itself.

---
 include/net/udp.h      |    2 +-
 net/ipv4/udp_offload.c |    6 ++++--
 net/ipv6/udp_offload.c |    2 +-
 3 files changed, 6 insertions(+), 4 deletions(-)
include/net/udp.h +1 -1
index 05990746810e..8bd83b044ecd 100644
@@ -176,7 +176,7 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
 
 struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 				  netdev_features_t features,
-				  unsigned int mss, __sum16 check);
+				  __sum16 check);
 
 static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
 {
net/ipv4/udp_offload.c +4 -2
index 006257092f06..c1afcd2f1a76 100644
@@ -189,14 +189,16 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
 
 struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 				  netdev_features_t features,
-				  unsigned int mss, __sum16 check)
+				  __sum16 check)
 {
 	struct sock *sk = gso_skb->sk;
 	unsigned int sum_truesize = 0;
 	struct sk_buff *segs, *seg;
 	unsigned int hdrlen;
 	struct udphdr *uh;
+	unsigned int mss;
 
+	mss = skb_shinfo(gso_skb)->gso_size;
 	if (gso_skb->len <= sizeof(*uh) + mss)
 		return ERR_PTR(-EINVAL);
 
@@ -244,7 +246,7 @@ static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb,
 	if (!can_checksum_protocol(features, htons(ETH_P_IP)))
 		return ERR_PTR(-EIO);
 
-	return __udp_gso_segment(gso_skb, features, mss,
+	return __udp_gso_segment(gso_skb, features,
 				 udp_v4_check(sizeof(struct udphdr) + mss,
 					      iph->saddr, iph->daddr, 0));
 }
net/ipv6/udp_offload.c +1 -1
index f7b85b1e6b3e..dea03ec09715 100644
@@ -26,7 +26,7 @@ static struct sk_buff *__udp6_gso_segment(struct sk_buff *gso_skb,
 	if (!can_checksum_protocol(features, htons(ETH_P_IPV6)))
 		return ERR_PTR(-EIO);
 
-	return __udp_gso_segment(gso_skb, features, mss,
+	return __udp_gso_segment(gso_skb, features,
 				 udp_v6_check(sizeof(struct udphdr) + mss,
 					      &ip6h->saddr, &ip6h->daddr, 0));
 }
#3 lore raw

PATCH v3 3/6 udp: Do not pass checksum as a parameter to GSO segmentation

In reply to Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch is meant to allow us to avoid having to recompute the checksum from scratch and have it passed as a parameter.

Instead of taking that approach we can take advantage of the fact that the length that was used to compute the existing checksum is included in the UDP header.

Finally to avoid the need to invert the result we can just call csum16_add and csum16_sub directly. By doing this we can avoid a number of instructions in the loop that is handling segmentation.

---
 include/net/udp.h      |    3 +--
 net/ipv4/udp_offload.c |   32 ++++++++++++++++++--------------
 net/ipv6/udp_offload.c |    7 +------
 3 files changed, 20 insertions(+), 22 deletions(-)
include/net/udp.h +1 -2
index 8bd83b044ecd..9289b6425032 100644
@@ -175,8 +175,7 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
 int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
 
 struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
-				  netdev_features_t features,
-				  __sum16 check);
+				  netdev_features_t features);
 
 static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
 {
net/ipv4/udp_offload.c +18 -14
index c1afcd2f1a76..92c182e99ddc 100644
@@ -188,8 +188,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
 EXPORT_SYMBOL(skb_udp_tunnel_segment);
 
 struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
-				  netdev_features_t features,
-				  __sum16 check)
+				  netdev_features_t features)
 {
 	struct sock *sk = gso_skb->sk;
 	unsigned int sum_truesize = 0;
@@ -197,6 +196,8 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	unsigned int hdrlen;
 	struct udphdr *uh;
 	unsigned int mss;
+	__sum16 check;
+	__be16 newlen;
 
 	mss = skb_shinfo(gso_skb)->gso_size;
 	if (gso_skb->len <= sizeof(*uh) + mss)
@@ -215,17 +216,25 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 		return segs;
 	}
 
+	uh = udp_hdr(segs);
+
+	/* compute checksum adjustment based on old length versus new */
+	newlen = htons(sizeof(*uh) + mss);
+	check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
+
 	for (seg = segs; seg; seg = seg->next) {
 		uh = udp_hdr(seg);
-		uh->len = htons(seg->len - hdrlen);
-		uh->check = check;
 
 		/* last packet can be partial gso_size */
-		if (!seg->next)
-			csum_replace2(&uh->check, htons(mss),
-				      htons(seg->len - hdrlen - sizeof(*uh)));
+		if (!seg->next) {
+			newlen = htons(seg->len - hdrlen);
+			check = csum16_add(csum16_sub(uh->check, uh->len),
+					   newlen);
+		}
+
+		uh->len = newlen;
+		uh->check = check;
 
-		uh->check = ~uh->check;
 		seg->destructor = sock_wfree;
 		seg->sk = sk;
 		sum_truesize += seg->truesize;
@@ -240,15 +249,10 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb,
 					  netdev_features_t features)
 {
-	const struct iphdr *iph = ip_hdr(gso_skb);
-	unsigned int mss = skb_shinfo(gso_skb)->gso_size;
-
 	if (!can_checksum_protocol(features, htons(ETH_P_IP)))
 		return ERR_PTR(-EIO);
 
-	return __udp_gso_segment(gso_skb, features,
-				 udp_v4_check(sizeof(struct udphdr) + mss,
-					      iph->saddr, iph->daddr, 0));
+	return __udp_gso_segment(gso_skb, features);
 }
 
 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
net/ipv6/udp_offload.c +1 -6
index dea03ec09715..61e34f1d2fa2 100644
@@ -20,15 +20,10 @@
 static struct sk_buff *__udp6_gso_segment(struct sk_buff *gso_skb,
 					  netdev_features_t features)
 {
-	const struct ipv6hdr *ip6h = ipv6_hdr(gso_skb);
-	unsigned int mss = skb_shinfo(gso_skb)->gso_size;
-
 	if (!can_checksum_protocol(features, htons(ETH_P_IPV6)))
 		return ERR_PTR(-EIO);
 
-	return __udp_gso_segment(gso_skb, features,
-				 udp_v6_check(sizeof(struct udphdr) + mss,
-					      &ip6h->saddr, &ip6h->daddr, 0));
+	return __udp_gso_segment(gso_skb, features);
 }
 
 static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
maintainer Acked-by on v3 3/6
#4 lore raw

Re: [net-next PATCH v3 3/6] udp: Do not pass checksum as a parameter to GSO segmentation

Quoted 9 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch is meant to allow us to avoid having to recompute the checksum from scratch and have it passed as a parameter.

Instead of taking that approach we can take advantage of the fact that the length that was used to compute the existing checksum is included in the UDP header.

Quoted 5 lines

Finally to avoid the need to invert the result we can just call csum16_add and csum16_sub directly. By doing this we can avoid a number of instructions in the loop that is handling segmentation.

Small aside: instead of open-coding the csum16 operations, it might make sense to define a pseudo_csum_replace2 function and convert csum_replace2 to call that and only do the inversion.

#5 lore raw

PATCH v3 4/6 udp: Partially unroll handling of first segment and last segment

In reply to Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch allows us to take care of unrolling the first segment and the last segment of the loop for processing the segmented skb. Part of the motivation for this is that it makes it easier to process the fact that the first fame and all of the frames in between should be mostly identical in terms of header data, and the last frame has differences in the length and partial checksum.

In addition I am dropping the header length calculation since we don't really need it for anything but the last frame and it can be easily obtained by just pulling the data_len and offset of tail from the transport header.

---
 net/ipv4/udp_offload.c |   33 +++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)
net/ipv4/udp_offload.c +19 -14
index 92c182e99ddc..b15c78ac3f23 100644
@@ -193,7 +193,6 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	struct sock *sk = gso_skb->sk;
 	unsigned int sum_truesize = 0;
 	struct sk_buff *segs, *seg;
-	unsigned int hdrlen;
 	struct udphdr *uh;
 	unsigned int mss;
 	__sum16 check;
@@ -203,7 +202,6 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	if (gso_skb->len <= sizeof(*uh) + mss)
 		return ERR_PTR(-EINVAL);
 
-	hdrlen = gso_skb->data - skb_mac_header(gso_skb);
 	skb_pull(gso_skb, sizeof(*uh));
 
 	/* clear destructor to avoid skb_segment assigning it to tail */
@@ -216,30 +214,37 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 		return segs;
 	}
 
-	uh = udp_hdr(segs);
+	seg = segs;
+	uh = udp_hdr(seg);
 
 	/* compute checksum adjustment based on old length versus new */
 	newlen = htons(sizeof(*uh) + mss);
 	check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
 
-	for (seg = segs; seg; seg = seg->next) {
-		uh = udp_hdr(seg);
+	for (;;) {
+		seg->destructor = sock_wfree;
+		seg->sk = sk;
+		sum_truesize += seg->truesize;
 
-		/* last packet can be partial gso_size */
-		if (!seg->next) {
-			newlen = htons(seg->len - hdrlen);
-			check = csum16_add(csum16_sub(uh->check, uh->len),
-					   newlen);
-		}
+		if (!seg->next)
+			break;
 
 		uh->len = newlen;
 		uh->check = check;
 
-		seg->destructor = sock_wfree;
-		seg->sk = sk;
-		sum_truesize += seg->truesize;
+		seg = seg->next;
+		uh = udp_hdr(seg);
 	}
 
+	/* last packet can be partial gso_size, account for that in checksum */
+	newlen = htons(skb_tail_pointer(seg) - skb_transport_header(seg) +
+		       seg->data_len);
+	check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
+
+	uh->len = newlen;
+	uh->check = check;
+
+	/* update refcount for the packet */
 	refcount_add(sum_truesize - gso_skb->truesize, &sk->sk_wmem_alloc);
 
 	return segs;
Reviewed-by on v3 4/6
#6 lore raw

Re: [net-next PATCH v3 4/6] udp: Partially unroll handling of first segment and last segment

Quoted 9 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch allows us to take care of unrolling the first segment and the last segment of the loop for processing the segmented skb. Part of the motivation for this is that it makes it easier to process the fact that the first fame and all of the frames in between should be mostly identical in terms of header data, and the last frame has differences in the length and partial checksum.

Quoted 6 lines

In addition I am dropping the header length calculation since we don't really need it for anything but the last frame and it can be easily obtained by just pulling the data_len and offset of tail from the transport header.

maintainer
#7 lore raw

Re: [net-next PATCH v3 4/6] udp: Partially unroll handling of first segment and last segment

In reply to Alexander Duyck
Quoted 9 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch allows us to take care of unrolling the first segment and the last segment of the loop for processing the segmented skb. Part of the motivation for this is that it makes it easier to process the fact that the first fame and all of the frames in between should be mostly identical in terms of header data, and the last frame has differences in the length and partial checksum.

Quoted 6 lines

In addition I am dropping the header length calculation since we don't really need it for anything but the last frame and it can be easily obtained by just pulling the data_len and offset of tail from the transport header.

I'm not a fan of the more complicated control flow, as I pointed out before. It only seems to save one assignment to uh from segs.

Both follow-up patches are now more complex, because they need to add the same code in two locations.

maintainer
#8 lore raw

Re: [net-next PATCH v3 4/6] udp: Partially unroll handling of first segment and last segment

Quoted 16 lines · Willem de Bruijn
Quoted 14 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch allows us to take care of unrolling the first segment and the last segment of the loop for processing the segmented skb. Part of the motivation for this is that it makes it easier to process the fact that the first fame and all of the frames in between should be mostly identical in terms of header data, and the last frame has differences in the length and partial checksum.

In addition I am dropping the header length calculation since we don't really need it for anything but the last frame and it can be easily obtained by just pulling the data_len and offset of tail from the transport header.

Quoted 7 lines
Quoted 1 line

I'm not a fan of the more complicated control flow, as I pointed out before. It only seems to save one assignment to uh from segs.

Both follow-up patches are now more complex, because they need to add the same code in two locations.

With that said, if you feel strongly, I don't object.

The removal of hdrlen and simplification of arguments is definitely an improvement.

#9 lore raw

Re: [net-next PATCH v3 4/6] udp: Partially unroll handling of first segment and last segment

Quoted 23 lines · Willem de Bruijn
Quoted 21 lines · Willem de Bruijn
Quoted 9 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch allows us to take care of unrolling the first segment and the last segment of the loop for processing the segmented skb. Part of the motivation for this is that it makes it easier to process the fact that the first fame and all of the frames in between should be mostly identical in terms of header data, and the last frame has differences in the length and partial checksum.

Quoted 6 lines

In addition I am dropping the header length calculation since we don't really need it for anything but the last frame and it can be easily obtained by just pulling the data_len and offset of tail from the transport header.

I'm not a fan of the more complicated control flow, as I pointed out before. It only seems to save one assignment to uh from segs.

Quoted 7 lines
Quoted 2 lines

Both follow-up patches are now more complex, because they need to add the same code in two locations.

With that said, if you feel strongly, I don't object.

The removal of hdrlen and simplification of arguments is definitely an improvement.

Thanks for being understanding about this.

My preference is to keep the loop unrolled as it is since that way it is not too different from the way we handle this for TCP so it will maintenance of the two easier. Otherwise I have to add a bunch of conditional checks inside the loop.

The other advantage to unrolling it as I did is that I don't have to deal with a ton of extra indentation for an if statement inside of a while loop.

- Alex

maintainer Acked-by
#10 lore raw

Re: [net-next PATCH v3 4/6] udp: Partially unroll handling of first segment and last segment

Quoted 15 lines · Alexander Duyck
Quoted 13 lines · Willem de Bruijn
Quoted 11 lines · Willem de Bruijn
Quoted 9 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch allows us to take care of unrolling the first segment and the last segment of the loop for processing the segmented skb. Part of the motivation for this is that it makes it easier to process the fact that the first fame and all of the frames in between should be mostly identical in terms of header data, and the last frame has differences in the length and partial checksum.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

In addition I am dropping the header length calculation since we don't really need it for anything but the last frame and it can be easily obtained by just pulling the data_len and offset of tail from the transport header.

Quoted 13 lines
Quoted 10 lines
Quoted 5 lines

I'm not a fan of the more complicated control flow, as I pointed out before. It only seems to save one assignment to uh from segs.

Both follow-up patches are now more complex, because they need to add the same code in two locations.

With that said, if you feel strongly, I don't object.

The removal of hdrlen and simplification of arguments is definitely an improvement.

Thanks for being understanding about this.

Quoted 8 lines

My preference is to keep the loop unrolled as it is since that way it is not too different from the way we handle this for TCP so it will maintenance of the two easier. Otherwise I have to add a bunch of conditional checks inside the loop.

The other advantage to unrolling it as I did is that I don't have to deal with a ton of extra indentation for an if statement inside of a while loop.

Both good reasons. Thanks a lot for the overall cleanup.

#11 lore raw

PATCH v3 5/6 udp: Add support for software checksum and GSO_PARTIAL with GSO offload

In reply to Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch adds support for a software provided checksum and GSO_PARTIAL segmentation support. With this we can offload UDP segmentation on devices that only have partial support for tunnels.

Since we are no longer needing the hardware checksum we can drop the checks in the segmentation code that were verifying if it was present.

---
 net/ipv4/udp_offload.c |   29 +++++++++++++++++++----------
 net/ipv6/udp_offload.c |   11 +----------
 2 files changed, 20 insertions(+), 20 deletions(-)
net/ipv4/udp_offload.c +19 -10
index b15c78ac3f23..d4f2daca0c33 100644
@@ -214,6 +214,13 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 		return segs;
 	}
 
+	/* GSO partial and frag_list segmentation only requires splitting
+	 * the frame into an MSS multiple and possibly a remainder, both
+	 * cases return a GSO skb. So update the mss now.
+	 */
+	if (skb_is_gso(segs))
+		mss *= skb_shinfo(segs)->gso_segs;
+
 	seg = segs;
 	uh = udp_hdr(seg);
 
@@ -232,6 +239,12 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 		uh->len = newlen;
 		uh->check = check;
 
+		if (seg->ip_summed == CHECKSUM_PARTIAL)
+			gso_reset_checksum(seg, ~check);
+		else
+			uh->check = gso_make_checksum(seg, ~check) ? :
+				    CSUM_MANGLED_0;
+
 		seg = seg->next;
 		uh = udp_hdr(seg);
 	}
@@ -244,6 +257,11 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	uh->len = newlen;
 	uh->check = check;
 
+	if (seg->ip_summed == CHECKSUM_PARTIAL)
+		gso_reset_checksum(seg, ~check);
+	else
+		uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;
+
 	/* update refcount for the packet */
 	refcount_add(sum_truesize - gso_skb->truesize, &sk->sk_wmem_alloc);
 
@@ -251,15 +269,6 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 }
 EXPORT_SYMBOL_GPL(__udp_gso_segment);
 
-static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb,
-					  netdev_features_t features)
-{
-	if (!can_checksum_protocol(features, htons(ETH_P_IP)))
-		return ERR_PTR(-EIO);
-
-	return __udp_gso_segment(gso_skb, features);
-}
-
 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
 					 netdev_features_t features)
 {
@@ -283,7 +292,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
 		goto out;
 
 	if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
-		return __udp4_gso_segment(skb, features);
+		return __udp_gso_segment(skb, features);
 
 	mss = skb_shinfo(skb)->gso_size;
 	if (unlikely(skb->len <= mss))
net/ipv6/udp_offload.c +1 -10
index 61e34f1d2fa2..03a2ff3fe1e6 100644
@@ -17,15 +17,6 @@
 #include <net/ip6_checksum.h>
 #include "ip6_offload.h"
 
-static struct sk_buff *__udp6_gso_segment(struct sk_buff *gso_skb,
-					  netdev_features_t features)
-{
-	if (!can_checksum_protocol(features, htons(ETH_P_IPV6)))
-		return ERR_PTR(-EIO);
-
-	return __udp_gso_segment(gso_skb, features);
-}
-
 static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
 					 netdev_features_t features)
 {
@@ -58,7 +49,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
 			goto out;
 
 		if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
-			return __udp6_gso_segment(skb, features);
+			return __udp_gso_segment(skb, features);
 
 		/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
 		 * do checksum of UDP packets sent as multiple IP fragments.
maintainer Acked-by on v3 5/6
#12 lore raw

Re: [net-next PATCH v3 5/6] udp: Add support for software checksum and GSO_PARTIAL with GSO offload

Quoted 10 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch adds support for a software provided checksum and GSO_PARTIAL segmentation support. With this we can offload UDP segmentation on devices that only have partial support for tunnels.

Since we are no longer needing the hardware checksum we can drop the checks in the segmentation code that were verifying if it was present.

#13 lore raw

PATCH v3 6/6 udp: Do not copy destructor if one is not present

In reply to Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch makes it so that if a destructor is not present we avoid trying to update the skb socket or any reference counting that would be associated with the NULL socket and/or descriptor. By doing this we can support traffic coming from another namespace without any issues.

---
 net/ipv4/udp_offload.c |   22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)
net/ipv4/udp_offload.c +14 -8
index d4f2daca0c33..ede2a7305b90 100644
@@ -195,6 +195,7 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	struct sk_buff *segs, *seg;
 	struct udphdr *uh;
 	unsigned int mss;
+	bool copy_dtor;
 	__sum16 check;
 	__be16 newlen;
 
@@ -205,12 +206,14 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	skb_pull(gso_skb, sizeof(*uh));
 
 	/* clear destructor to avoid skb_segment assigning it to tail */
-	WARN_ON_ONCE(gso_skb->destructor != sock_wfree);
-	gso_skb->destructor = NULL;
+	copy_dtor = gso_skb->destructor == sock_wfree;
+	if (copy_dtor)
+		gso_skb->destructor = NULL;
 
 	segs = skb_segment(gso_skb, features);
 	if (unlikely(IS_ERR_OR_NULL(segs))) {
-		gso_skb->destructor = sock_wfree;
+		if (copy_dtor)
+			gso_skb->destructor = sock_wfree;
 		return segs;
 	}
 
@@ -229,9 +232,11 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 	check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
 
 	for (;;) {
-		seg->destructor = sock_wfree;
-		seg->sk = sk;
-		sum_truesize += seg->truesize;
+		if (copy_dtor) {
+			seg->destructor = sock_wfree;
+			seg->sk = sk;
+			sum_truesize += seg->truesize;
+		}
 
 		if (!seg->next)
 			break;
@@ -263,8 +268,9 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
 		uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;
 
 	/* update refcount for the packet */
-	refcount_add(sum_truesize - gso_skb->truesize, &sk->sk_wmem_alloc);
-
+	if (copy_dtor)
+		refcount_add(sum_truesize - gso_skb->truesize,
+			     &sk->sk_wmem_alloc);
 	return segs;
 }
 EXPORT_SYMBOL_GPL(__udp_gso_segment);
Reviewed-by on v3 6/6
#14 lore raw

Re: [net-next PATCH v3 6/6] udp: Do not copy destructor if one is not present

Quoted 9 lines · Alexander Duyck

From: Alexander Duyck <[email protected]>

This patch makes it so that if a destructor is not present we avoid trying to update the skb socket or any reference counting that would be associated with the NULL socket and/or descriptor. By doing this we can support traffic coming from another namespace without any issues.