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

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: Jakub Kicinski <kuba@kernel.org>

+65 -7
+1 -2
net/ipv4/udp.c
··· 938 938 kfree_skb(skb); 939 939 return -EINVAL; 940 940 } 941 - if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || 942 - dst_xfrm(skb_dst(skb))) { 941 + if (is_udplite || dst_xfrm(skb_dst(skb))) { 943 942 kfree_skb(skb); 944 943 return -EIO; 945 944 }
+8
net/ipv4/udp_offload.c
··· 357 357 else 358 358 uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0; 359 359 360 + /* On the TX path, CHECKSUM_NONE and CHECKSUM_UNNECESSARY have the same 361 + * meaning. However, check for bad offloads in the GSO stack expects the 362 + * latter, if the checksum was calculated in software. To vouch for the 363 + * segment skbs we actually need to set it on the gso_skb. 364 + */ 365 + if (gso_skb->ip_summed == CHECKSUM_NONE) 366 + gso_skb->ip_summed = CHECKSUM_UNNECESSARY; 367 + 360 368 /* update refcount for the packet */ 361 369 if (copy_dtor) { 362 370 int delta = sum_truesize - gso_skb->truesize;
+1 -2
net/ipv6/udp.c
··· 1257 1257 kfree_skb(skb); 1258 1258 return -EINVAL; 1259 1259 } 1260 - if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || 1261 - dst_xfrm(skb_dst(skb))) { 1260 + if (is_udplite || dst_xfrm(skb_dst(skb))) { 1262 1261 kfree_skb(skb); 1263 1262 return -EIO; 1264 1263 }
+12 -3
tools/testing/selftests/net/udpgso.c
··· 53 53 static bool cfg_do_connected; 54 54 static bool cfg_do_connectionless; 55 55 static bool cfg_do_msgmore; 56 + static bool cfg_do_recv = true; 56 57 static bool cfg_do_setsockopt; 57 58 static int cfg_specific_test_id = -1; 58 59 ··· 415 414 if (!sent) 416 415 return; 417 416 417 + if (!cfg_do_recv) 418 + return; 419 + 418 420 if (test->gso_len) 419 421 mss = test->gso_len; 420 422 else ··· 468 464 if (fdr == -1) 469 465 error(1, errno, "socket r"); 470 466 471 - if (bind(fdr, addr, alen)) 472 - error(1, errno, "bind"); 467 + if (cfg_do_recv) { 468 + if (bind(fdr, addr, alen)) 469 + error(1, errno, "bind"); 470 + } 473 471 474 472 /* Have tests fail quickly instead of hang */ 475 473 if (setsockopt(fdr, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) ··· 530 524 { 531 525 int c; 532 526 533 - while ((c = getopt(argc, argv, "46cCmst:")) != -1) { 527 + while ((c = getopt(argc, argv, "46cCmRst:")) != -1) { 534 528 switch (c) { 535 529 case '4': 536 530 cfg_do_ipv4 = true; ··· 546 540 break; 547 541 case 'm': 548 542 cfg_do_msgmore = true; 543 + break; 544 + case 'R': 545 + cfg_do_recv = false; 549 546 break; 550 547 case 's': 551 548 cfg_do_setsockopt = true;
+43
tools/testing/selftests/net/udpgso.sh
··· 27 27 ip route add local fd00::1/128 table local dev lo mtu 1500 28 28 } 29 29 30 + setup_dummy_sink() { 31 + ip link add name sink mtu 1500 type dummy 32 + ip addr add dev sink 10.0.0.0/24 33 + ip addr add dev sink fd00::2/64 nodad 34 + ip link set dev sink up 35 + } 36 + 37 + test_hw_gso_hw_csum() { 38 + setup_dummy_sink 39 + ethtool -K sink tx-checksum-ip-generic on >/dev/null 40 + ethtool -K sink tx-udp-segmentation on >/dev/null 41 + } 42 + 43 + test_sw_gso_hw_csum() { 44 + setup_dummy_sink 45 + ethtool -K sink tx-checksum-ip-generic on >/dev/null 46 + ethtool -K sink tx-udp-segmentation off >/dev/null 47 + } 48 + 49 + test_sw_gso_sw_csum() { 50 + setup_dummy_sink 51 + ethtool -K sink tx-checksum-ip-generic off >/dev/null 52 + ethtool -K sink tx-udp-segmentation off >/dev/null 53 + } 54 + 30 55 if [ "$#" -gt 0 ]; then 31 56 "$1" 32 57 shift 2 # pop "test_*" arg and "--" delimiter ··· 81 56 82 57 echo "ipv6 msg_more" 83 58 ./in_netns.sh "$0" test_dev_mtu -- ./udpgso -6 -C -m 59 + 60 + echo "ipv4 hw-gso hw-csum" 61 + ./in_netns.sh "$0" test_hw_gso_hw_csum -- ./udpgso -4 -C -R 62 + 63 + echo "ipv6 hw-gso hw-csum" 64 + ./in_netns.sh "$0" test_hw_gso_hw_csum -- ./udpgso -6 -C -R 65 + 66 + echo "ipv4 sw-gso hw-csum" 67 + ./in_netns.sh "$0" test_sw_gso_hw_csum -- ./udpgso -4 -C -R 68 + 69 + echo "ipv6 sw-gso hw-csum" 70 + ./in_netns.sh "$0" test_sw_gso_hw_csum -- ./udpgso -6 -C -R 71 + 72 + echo "ipv4 sw-gso sw-csum" 73 + ./in_netns.sh "$0" test_sw_gso_sw_csum -- ./udpgso -4 -C -R 74 + 75 + echo "ipv6 sw-gso sw-csum" 76 + ./in_netns.sh "$0" test_sw_gso_sw_csum -- ./udpgso -6 -C -R