Subject: Re: [PATCH bpf-next v1 6/7] selftests/bpf: add splice_read tests for sockmap

From: Mykyta Yatsenko <[email-masked]>
Date: Fri, 06 Mar 2026 17:25:01 +0000

Jiayuan Chen <[email-masked]> writes:

> Add splice_read coverage to sockmap_basic and sockmap_strp selftests.
> Each test suite now runs twice: once with normal recv_timeout() and
> once with splice-based reads, verifying that data read via splice(2)
> through a pipe produces identical results.
>
> A recv_timeout_with_splice() helper is added to sockmap_helpers.h
> that creates a temporary pipe, splices data from the socket into
> the pipe, then reads from the pipe into the user buffer. MSG_PEEK
> calls fall back to native recv since splice does not support peek.
> Non-TCP sockets also fall back to native recv.
>
> The splice subtests are distinguished by appending " splice" to
> each subtest name via a test__start_subtest macro override.
>
> ./test_progs -a sockmap_*
> ...
> Summary: 5/830 PASSED, 0 SKIPPED, 0 FAILED
>
> Signed-off-by: Jiayuan Chen <[email-masked]>
> ---
>  .../selftests/bpf/prog_tests/sockmap_basic.c  | 28 ++++++++-
>  .../bpf/prog_tests/sockmap_helpers.h          | 62 +++++++++++++++++++
>  .../selftests/bpf/prog_tests/sockmap_strp.c   | 28 ++++++++-
>  3 files changed, 116 insertions(+), 2 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> index dd3c757859f6..ea0b49ec9a93 100644
> --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> @@ -18,6 +18,23 @@
>  
>  #include "sockmap_helpers.h"
>  
> +static bool use_splice;
> +
> +static bool __start_subtest(const char *name)
> +{
> +	if (!use_splice)
> +		return (test__start_subtest)(name);
> +
> +	char buf[MAX_TEST_NAME];
> +
> +	snprintf(buf, sizeof(buf), "%s splice", name);
> +	return (test__start_subtest)(buf);
> +}
> +
> +#define test__start_subtest(name) __start_subtest(name)
> +#define recv_timeout(fd, buf, len, flags, timeout) \
> +	recv_timeout_with_splice(fd, buf, len, flags, timeout, use_splice)
Not sure if this is a good idea to redefine test__start_subtest and
recv_timeout.
It looks like recv_timeout() is called 5 times in this file, so maybe
handling recv_timeout_with_splice() explicitly won't be too messy. 
> +
>  #define TCP_REPAIR		19	/* TCP sock is under repair right now */
>  
>  #define TCP_REPAIR_ON		1
> @@ -1314,7 +1331,7 @@ static void test_sockmap_multi_channels(int sotype)
>  	test_sockmap_pass_prog__destroy(skel);
>  }
>  
> -void test_sockmap_basic(void)
> +static void __test_sockmap_basic(void)
>  {
>  	if (test__start_subtest("sockmap create_update_free"))
>  		test_sockmap_create_update_free(BPF_MAP_TYPE_SOCKMAP);
> @@ -1391,3 +1408,12 @@ void test_sockmap_basic(void)
>  	if (test__start_subtest("sockmap udp multi channels"))
>  		test_sockmap_multi_channels(SOCK_DGRAM);
>  }
> +
> +void test_sockmap_basic(void)
> +{
> +	use_splice = false;
> +	__test_sockmap_basic();
> +
> +	use_splice = true;
> +	__test_sockmap_basic();
> +}
> diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h b/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h
> index d815efac52fd..1f0da657243f 100644
> --- a/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h
> +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h
> @@ -80,4 +80,66 @@ static inline int add_to_sockmap(int mapfd, int fd1, int fd2)
>  	return xbpf_map_update_elem(mapfd, &u32(1), &u64(fd2), BPF_NOEXIST);
>  }
>  
> +static inline ssize_t recv_timeout_with_splice(int fd, void *buf, size_t len,
> +					       int flags,
> +					       unsigned int timeout_sec,
> +					       bool do_splice)
> +{
> +	ssize_t total = 0;
> +	int pipefd[2];
> +	int fl;
> +
> +	int sotype, protocol;
> +	socklen_t optlen = sizeof(sotype);
> +
> +	if (!do_splice || (flags & MSG_PEEK) ||
> +	    getsockopt(fd, SOL_SOCKET, SO_TYPE, &sotype, &optlen) ||
> +	    sotype != SOCK_STREAM ||
> +	    getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &optlen) ||
> +	    protocol != IPPROTO_TCP)
> +		return recv_timeout(fd, buf, len, flags, timeout_sec);
> +
> +	if (poll_read(fd, timeout_sec))
> +		return -1;
> +
> +	if (pipe(pipefd) < 0)
> +		return -1;
> +
> +	/*
> +	 * tcp_splice_read() only checks sock->file->f_flags for
> +	 * O_NONBLOCK, ignoring SPLICE_F_NONBLOCK for the socket
> +	 * side timeout. Set O_NONBLOCK on the fd so the loop won't
> +	 * block forever when no more data is available.
> +	 */
> +	fl = fcntl(fd, F_GETFL);
> +	fcntl(fd, F_SETFL, fl | O_NONBLOCK);
> +
> +	/*
> +	 * Pipe has limited buffer slots (default 16), so a single
> +	 * splice may not transfer all requested bytes. Loop until
> +	 * we've read enough or no more data is available.
> +	 */
> +	while (total < (ssize_t)len) {
> +		ssize_t spliced, n;
> +
> +		spliced = splice(fd, NULL, pipefd[1], NULL, len - total,
> +				 SPLICE_F_NONBLOCK);
> +		if (spliced <= 0)
> +			break;
> +
> +		n = read(pipefd[0], buf + total, spliced);
> +		if (n <= 0)
> +			break;
> +
> +		total += n;
> +	}
> +
> +	fcntl(fd, F_SETFL, fl);
> +
> +	close(pipefd[0]);
> +	close(pipefd[1]);
> +
> +	return total > 0 ? total : -1;
> +}
> +
>  #endif // __SOCKMAP_HELPERS__
> diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c b/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
> index 621b3b71888e..2226399eee0d 100644
> --- a/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
> +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
> @@ -6,6 +6,23 @@
>  #include "test_skmsg_load_helpers.skel.h"
>  #include "test_sockmap_strp.skel.h"
>  
> +static bool use_splice;
> +
> +static bool __start_subtest(const char *name)
> +{
> +	if (!use_splice)
> +		return (test__start_subtest)(name);
> +
> +	char buf[MAX_TEST_NAME];
> +
> +	snprintf(buf, sizeof(buf), "%s splice", name);
> +	return (test__start_subtest)(buf);
> +}
> +
> +#define test__start_subtest(name) __start_subtest(name)
> +#define recv_timeout(fd, buf, len, flags, timeout) \
> +	recv_timeout_with_splice(fd, buf, len, flags, timeout, use_splice)
> +
>  #define STRP_PKT_HEAD_LEN 4
>  #define STRP_PKT_BODY_LEN 6
>  #define STRP_PKT_FULL_LEN (STRP_PKT_HEAD_LEN + STRP_PKT_BODY_LEN)
> @@ -431,7 +448,7 @@ static void test_sockmap_strp_verdict(int family, int sotype)
>  	test_sockmap_strp__destroy(strp);
>  }
>  
> -void test_sockmap_strp(void)
> +static void __test_sockmap_strp(void)
>  {
>  	if (test__start_subtest("sockmap strp tcp pass"))
>  		test_sockmap_strp_pass(AF_INET, SOCK_STREAM, false);
> @@ -452,3 +469,12 @@ void test_sockmap_strp(void)
>  	if (test__start_subtest("sockmap strp tcp dispatch"))
>  		test_sockmap_strp_dispatch_pkt(AF_INET, SOCK_STREAM);
>  }
> +
> +void test_sockmap_strp(void)
> +{
> +	use_splice = false;
> +	__test_sockmap_strp();
> +
> +	use_splice = true;
> +	__test_sockmap_strp();
> +}
> -- 
> 2.43.0