Commit db2dede2 authored by Jakub Kicinski's avatar Jakub Kicinski

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.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 748e3bbf 3e400219
...@@ -938,8 +938,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4, ...@@ -938,8 +938,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || if (is_udplite || dst_xfrm(skb_dst(skb))) {
dst_xfrm(skb_dst(skb))) {
kfree_skb(skb); kfree_skb(skb);
return -EIO; return -EIO;
} }
......
...@@ -357,6 +357,14 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb, ...@@ -357,6 +357,14 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
else else
uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0; 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 */ /* update refcount for the packet */
if (copy_dtor) { if (copy_dtor) {
int delta = sum_truesize - gso_skb->truesize; int delta = sum_truesize - gso_skb->truesize;
......
...@@ -1257,8 +1257,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, ...@@ -1257,8 +1257,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || if (is_udplite || dst_xfrm(skb_dst(skb))) {
dst_xfrm(skb_dst(skb))) {
kfree_skb(skb); kfree_skb(skb);
return -EIO; return -EIO;
} }
......
...@@ -53,6 +53,7 @@ static bool cfg_do_ipv6; ...@@ -53,6 +53,7 @@ static bool cfg_do_ipv6;
static bool cfg_do_connected; static bool cfg_do_connected;
static bool cfg_do_connectionless; static bool cfg_do_connectionless;
static bool cfg_do_msgmore; static bool cfg_do_msgmore;
static bool cfg_do_recv = true;
static bool cfg_do_setsockopt; static bool cfg_do_setsockopt;
static int cfg_specific_test_id = -1; static int cfg_specific_test_id = -1;
...@@ -414,6 +415,9 @@ static void run_one(struct testcase *test, int fdt, int fdr, ...@@ -414,6 +415,9 @@ static void run_one(struct testcase *test, int fdt, int fdr,
if (!sent) if (!sent)
return; return;
if (!cfg_do_recv)
return;
if (test->gso_len) if (test->gso_len)
mss = test->gso_len; mss = test->gso_len;
else else
...@@ -464,8 +468,10 @@ static void run_test(struct sockaddr *addr, socklen_t alen) ...@@ -464,8 +468,10 @@ static void run_test(struct sockaddr *addr, socklen_t alen)
if (fdr == -1) if (fdr == -1)
error(1, errno, "socket r"); error(1, errno, "socket r");
if (bind(fdr, addr, alen)) if (cfg_do_recv) {
error(1, errno, "bind"); if (bind(fdr, addr, alen))
error(1, errno, "bind");
}
/* Have tests fail quickly instead of hang */ /* Have tests fail quickly instead of hang */
if (setsockopt(fdr, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) if (setsockopt(fdr, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
...@@ -524,7 +530,7 @@ static void parse_opts(int argc, char **argv) ...@@ -524,7 +530,7 @@ static void parse_opts(int argc, char **argv)
{ {
int c; int c;
while ((c = getopt(argc, argv, "46cCmst:")) != -1) { while ((c = getopt(argc, argv, "46cCmRst:")) != -1) {
switch (c) { switch (c) {
case '4': case '4':
cfg_do_ipv4 = true; cfg_do_ipv4 = true;
...@@ -541,6 +547,9 @@ static void parse_opts(int argc, char **argv) ...@@ -541,6 +547,9 @@ static void parse_opts(int argc, char **argv)
case 'm': case 'm':
cfg_do_msgmore = true; cfg_do_msgmore = true;
break; break;
case 'R':
cfg_do_recv = false;
break;
case 's': case 's':
cfg_do_setsockopt = true; cfg_do_setsockopt = true;
break; break;
......
...@@ -27,6 +27,31 @@ test_route_mtu() { ...@@ -27,6 +27,31 @@ test_route_mtu() {
ip route add local fd00::1/128 table local dev lo mtu 1500 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 if [ "$#" -gt 0 ]; then
"$1" "$1"
shift 2 # pop "test_*" arg and "--" delimiter shift 2 # pop "test_*" arg and "--" delimiter
...@@ -56,3 +81,21 @@ echo "ipv4 msg_more" ...@@ -56,3 +81,21 @@ echo "ipv4 msg_more"
echo "ipv6 msg_more" echo "ipv6 msg_more"
./in_netns.sh "$0" test_dev_mtu -- ./udpgso -6 -C -m ./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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment