Commit db2dede2 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'lift-udp_segment-restriction-for-egress-via-device-w-o-csum-offload'

Jakub Sitnicki says:

====================
Lift UDP_SEGMENT restriction for egress via device w/o csum offload

This is a follow-up to an earlier question [1] if we can make UDP GSO work
with any egress device, even those with no checksum offload capability.
That's the default setup for TUN/TAP.

Because there is a change in behavior - sendmsg() does no longer return
EIO error - I'm submitting through net-next tree, rather than net,
as per Willem's advice.

[1] https://lore.kernel.org/netdev/87jzqsld6q.fsf@cloudflare.com/

v1: https://lore.kernel.org/r/20240622-linux-udpgso-v1-0-d2344157ab2a@cloudflare.com
====================

Link: https://patch.msgid.link/20240626-linux-udpgso-v2-0-422dfcbd6b48@cloudflare.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 748e3bbf 3e400219
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -938,8 +938,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
			kfree_skb(skb);
			return -EINVAL;
		}
		if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
		    dst_xfrm(skb_dst(skb))) {
		if (is_udplite || dst_xfrm(skb_dst(skb))) {
			kfree_skb(skb);
			return -EIO;
		}
+8 −0
Original line number Diff line number Diff line
@@ -357,6 +357,14 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
	else
		uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;

	/* On the TX path, CHECKSUM_NONE and CHECKSUM_UNNECESSARY have the same
	 * meaning. However, check for bad offloads in the GSO stack expects the
	 * latter, if the checksum was calculated in software. To vouch for the
	 * segment skbs we actually need to set it on the gso_skb.
	 */
	if (gso_skb->ip_summed == CHECKSUM_NONE)
		gso_skb->ip_summed = CHECKSUM_UNNECESSARY;

	/* update refcount for the packet */
	if (copy_dtor) {
		int delta = sum_truesize - gso_skb->truesize;
+1 −2
Original line number Diff line number Diff line
@@ -1257,8 +1257,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
			kfree_skb(skb);
			return -EINVAL;
		}
		if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
		    dst_xfrm(skb_dst(skb))) {
		if (is_udplite || dst_xfrm(skb_dst(skb))) {
			kfree_skb(skb);
			return -EIO;
		}
+12 −3
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ static bool cfg_do_ipv6;
static bool		cfg_do_connected;
static bool		cfg_do_connectionless;
static bool		cfg_do_msgmore;
static bool		cfg_do_recv = true;
static bool		cfg_do_setsockopt;
static int		cfg_specific_test_id = -1;

@@ -414,6 +415,9 @@ static void run_one(struct testcase *test, int fdt, int fdr,
	if (!sent)
		return;

	if (!cfg_do_recv)
		return;

	if (test->gso_len)
		mss = test->gso_len;
	else
@@ -464,8 +468,10 @@ static void run_test(struct sockaddr *addr, socklen_t alen)
	if (fdr == -1)
		error(1, errno, "socket r");

	if (cfg_do_recv) {
		if (bind(fdr, addr, alen))
			error(1, errno, "bind");
	}

	/* Have tests fail quickly instead of hang */
	if (setsockopt(fdr, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
@@ -524,7 +530,7 @@ static void parse_opts(int argc, char **argv)
{
	int c;

	while ((c = getopt(argc, argv, "46cCmst:")) != -1) {
	while ((c = getopt(argc, argv, "46cCmRst:")) != -1) {
		switch (c) {
		case '4':
			cfg_do_ipv4 = true;
@@ -541,6 +547,9 @@ static void parse_opts(int argc, char **argv)
		case 'm':
			cfg_do_msgmore = true;
			break;
		case 'R':
			cfg_do_recv = false;
			break;
		case 's':
			cfg_do_setsockopt = true;
			break;
+43 −0
Original line number Diff line number Diff line
@@ -27,6 +27,31 @@ test_route_mtu() {
	ip route add local fd00::1/128 table local dev lo mtu 1500
}

setup_dummy_sink() {
	ip link add name sink mtu 1500 type dummy
	ip addr add dev sink 10.0.0.0/24
	ip addr add dev sink fd00::2/64 nodad
	ip link set dev sink up
}

test_hw_gso_hw_csum() {
	setup_dummy_sink
	ethtool -K sink tx-checksum-ip-generic on >/dev/null
	ethtool -K sink tx-udp-segmentation on >/dev/null
}

test_sw_gso_hw_csum() {
	setup_dummy_sink
	ethtool -K sink tx-checksum-ip-generic on >/dev/null
	ethtool -K sink tx-udp-segmentation off >/dev/null
}

test_sw_gso_sw_csum() {
	setup_dummy_sink
	ethtool -K sink tx-checksum-ip-generic off >/dev/null
	ethtool -K sink tx-udp-segmentation off >/dev/null
}

if [ "$#" -gt 0 ]; then
	"$1"
	shift 2 # pop "test_*" arg and "--" delimiter
@@ -56,3 +81,21 @@ echo "ipv4 msg_more"

echo "ipv6 msg_more"
./in_netns.sh "$0" test_dev_mtu -- ./udpgso -6 -C -m

echo "ipv4 hw-gso hw-csum"
./in_netns.sh "$0" test_hw_gso_hw_csum -- ./udpgso -4 -C -R

echo "ipv6 hw-gso hw-csum"
./in_netns.sh "$0" test_hw_gso_hw_csum -- ./udpgso -6 -C -R

echo "ipv4 sw-gso hw-csum"
./in_netns.sh "$0" test_sw_gso_hw_csum -- ./udpgso -4 -C -R

echo "ipv6 sw-gso hw-csum"
./in_netns.sh "$0" test_sw_gso_hw_csum -- ./udpgso -6 -C -R

echo "ipv4 sw-gso sw-csum"
./in_netns.sh "$0" test_sw_gso_sw_csum -- ./udpgso -4 -C -R

echo "ipv6 sw-gso sw-csum"
./in_netns.sh "$0" test_sw_gso_sw_csum -- ./udpgso -6 -C -R