Jiayuan Chen <[email protected]> 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 protected]>
> ---
> .../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