linux/tools/testing/selftests/net/msg_zerocopy.sh
Willem de Bruijn 81d572a551 selftest: net: extend msg_zerocopy test with forwarding
Zerocopy skbs are converted to regular copy skbs when data is queued
to a local socket. This happens in the existing test with a sender and
receiver communicating over a veth device.

Zerocopy skbs are sent without copying if egressing a device. Verify
that this behavior is maintained even in the common container setup
where data is forwarded over a veth to the physical device.

Update msg_zerocopy.sh to

1. Have a dummy network device to simulate a physical device.
2. Have forwarding enabled between veth and dummy.
3. Add a tx-only test that sends out dummy via the forwarding path.
4. Verify the exitcode of the sender, which signals zerocopy success.

As dummy drops all packets, this cannot be a TCP connection. Test
the new case with unconnected UDP only.

Update msg_zerocopy.c to
- Accept an argument whether send with zerocopy is expected.
- Return an exitcode whether behavior matched that expectation.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20250630194312.1571410-3-willemdebruijn.kernel@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-02 15:07:16 -07:00

162 lines
4.2 KiB
Bash
Executable File

#!/bin/bash
#
# Send data between two processes across namespaces
# Run twice: once without and once with zerocopy
set -e
readonly DEV="veth0"
readonly DUMMY_DEV="dummy0"
readonly DEV_MTU=65535
readonly BIN="./msg_zerocopy"
readonly RAND="$(mktemp -u XXXXXX)"
readonly NSPREFIX="ns-${RAND}"
readonly NS1="${NSPREFIX}1"
readonly NS2="${NSPREFIX}2"
readonly LPREFIX4='192.168.1'
readonly RPREFIX4='192.168.2'
readonly LPREFIX6='fd'
readonly RPREFIX6='fc'
readonly path_sysctl_mem="net.core.optmem_max"
# No arguments: automated test
if [[ "$#" -eq "0" ]]; then
ret=0
$0 4 tcp -t 1 || ret=1
$0 6 tcp -t 1 || ret=1
$0 4 udp -t 1 || ret=1
$0 6 udp -t 1 || ret=1
[[ "$ret" == "0" ]] && echo "OK. All tests passed"
exit $ret
fi
# Argument parsing
if [[ "$#" -lt "2" ]]; then
echo "Usage: $0 [4|6] [tcp|udp|raw|raw_hdrincl|packet|packet_dgram] <args>"
exit 1
fi
readonly IP="$1"
shift
readonly TXMODE="$1"
shift
readonly EXTRA_ARGS="$@"
# Argument parsing: configure addresses
if [[ "${IP}" == "4" ]]; then
readonly SADDR="${LPREFIX4}.1"
readonly DADDR="${LPREFIX4}.2"
readonly DUMMY_ADDR="${RPREFIX4}.1"
readonly DADDR_TXONLY="${RPREFIX4}.2"
readonly MASK="24"
elif [[ "${IP}" == "6" ]]; then
readonly SADDR="${LPREFIX6}::1"
readonly DADDR="${LPREFIX6}::2"
readonly DUMMY_ADDR="${RPREFIX6}::1"
readonly DADDR_TXONLY="${RPREFIX6}::2"
readonly MASK="64"
readonly NODAD="nodad"
else
echo "Invalid IP version ${IP}"
exit 1
fi
# Argument parsing: select receive mode
#
# This differs from send mode for
# - packet: use raw recv, because packet receives skb clones
# - raw_hdrinc: use raw recv, because hdrincl is a tx-only option
case "${TXMODE}" in
'packet' | 'packet_dgram' | 'raw_hdrincl')
RXMODE='raw'
;;
*)
RXMODE="${TXMODE}"
;;
esac
# Start of state changes: install cleanup handler
cleanup() {
ip netns del "${NS2}"
ip netns del "${NS1}"
}
trap cleanup EXIT
# Create virtual ethernet pair between network namespaces
ip netns add "${NS1}"
ip netns add "${NS2}"
# Configure system settings
ip netns exec "${NS1}" sysctl -w -q "${path_sysctl_mem}=1000000"
ip netns exec "${NS2}" sysctl -w -q "${path_sysctl_mem}=1000000"
ip link add "${DEV}" mtu "${DEV_MTU}" netns "${NS1}" type veth \
peer name "${DEV}" mtu "${DEV_MTU}" netns "${NS2}"
ip link add "${DUMMY_DEV}" mtu "${DEV_MTU}" netns "${NS2}" type dummy
# Bring the devices up
ip -netns "${NS1}" link set "${DEV}" up
ip -netns "${NS2}" link set "${DEV}" up
ip -netns "${NS2}" link set "${DUMMY_DEV}" up
# Set fixed MAC addresses on the devices
ip -netns "${NS1}" link set dev "${DEV}" address 02:02:02:02:02:02
ip -netns "${NS2}" link set dev "${DEV}" address 06:06:06:06:06:06
# Add fixed IP addresses to the devices
ip -netns "${NS1}" addr add "${SADDR}/${MASK}" dev "${DEV}" ${NODAD}
ip -netns "${NS2}" addr add "${DADDR}/${MASK}" dev "${DEV}" ${NODAD}
ip -netns "${NS2}" addr add "${DUMMY_ADDR}/${MASK}" dev "${DUMMY_DEV}" ${NODAD}
ip -netns "${NS1}" route add default via "${DADDR}" dev "${DEV}"
ip -netns "${NS2}" route add default via "${DADDR_TXONLY}" dev "${DUMMY_DEV}"
ip netns exec "${NS2}" sysctl -wq net.ipv4.ip_forward=1
ip netns exec "${NS2}" sysctl -wq net.ipv6.conf.all.forwarding=1
# Optionally disable sg or csum offload to test edge cases
# ip netns exec "${NS1}" ethtool -K "${DEV}" sg off
ret=0
do_test() {
local readonly ARGS="$1"
# tx-rx test
# packets queued to a local socket are copied,
# sender notification has SO_EE_CODE_ZEROCOPY_COPIED.
echo -e "\nipv${IP} ${TXMODE} ${ARGS} tx-rx\n"
ip netns exec "${NS2}" "${BIN}" "-${IP}" -i "${DEV}" -t 2 -C 2 \
-S "${SADDR}" -D "${DADDR}" ${ARGS} -r "${RXMODE}" &
sleep 0.2
ip netns exec "${NS1}" "${BIN}" "-${IP}" -i "${DEV}" -t 1 -C 3 \
-S "${SADDR}" -D "${DADDR}" ${ARGS} "${TXMODE}" -Z 0 || ret=1
wait
# next test is unconnected tx to dummy0, cannot exercise with tcp
[[ "${TXMODE}" == "tcp" ]] && return
# tx-only test: send out dummy0
# packets leaving the host are not copied,
# sender notification does not have SO_EE_CODE_ZEROCOPY_COPIED.
echo -e "\nipv${IP} ${TXMODE} ${ARGS} tx-only\n"
ip netns exec "${NS1}" "${BIN}" "-${IP}" -i "${DEV}" -t 1 -C 3 \
-S "${SADDR}" -D "${DADDR_TXONLY}" ${ARGS} "${TXMODE}" -Z 1 || ret=1
}
do_test "${EXTRA_ARGS}"
do_test "-z ${EXTRA_ARGS}"
[[ "$ret" == "0" ]] && echo "OK"