selftests: mptcp: add splice io mode

This patch adds a new 'splice' io mode for mptcp_connect to test
the newly added read_sock() and splice_read() functions of MPTCP.

do_splice() efficiently transfers data directly between two file
descriptors (infd and outfd) without copying to userspace, using
Linux's splice() system call.

Usage:
	./mptcp_connect.sh -m splice

Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
Reviewed-by: Mat Martineau <martineau@kernel.org>
Co-developed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Link: https://patch.msgid.link/20260130-net-next-mptcp-splice-v2-5-31332ba70d7f@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Geliang Tang 2026-01-30 20:24:28 +01:00 committed by Jakub Kicinski
parent 22f3bd9bf8
commit 6a059c6bfb

View File

@ -52,6 +52,7 @@ enum cfg_mode {
CFG_MODE_POLL,
CFG_MODE_MMAP,
CFG_MODE_SENDFILE,
CFG_MODE_SPLICE,
};
enum cfg_peek {
@ -124,7 +125,7 @@ static void die_usage(void)
fprintf(stderr, "\t-j -- add additional sleep at connection start and tear down "
"-- for MPJ tests\n");
fprintf(stderr, "\t-l -- listens mode, accepts incoming connection\n");
fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n");
fprintf(stderr, "\t-m [poll|mmap|sendfile|splice] -- use poll(default)/mmap+write/sendfile/splice\n");
fprintf(stderr, "\t-M mark -- set socket packet mark\n");
fprintf(stderr, "\t-o option -- test sockopt <option>\n");
fprintf(stderr, "\t-p num -- use port num\n");
@ -935,6 +936,71 @@ static int copyfd_io_sendfile(int infd, int peerfd, int outfd,
return err;
}
static int do_splice(const int infd, const int outfd, const size_t len,
struct wstate *winfo)
{
ssize_t in_bytes, out_bytes;
int pipefd[2];
int err;
err = pipe(pipefd);
if (err) {
perror("pipe");
return 2;
}
again:
in_bytes = splice(infd, NULL, pipefd[1], NULL, len - winfo->total_len,
SPLICE_F_MOVE | SPLICE_F_MORE);
if (in_bytes < 0) {
perror("splice in");
err = 3;
} else if (in_bytes > 0) {
out_bytes = splice(pipefd[0], NULL, outfd, NULL, in_bytes,
SPLICE_F_MOVE | SPLICE_F_MORE);
if (out_bytes < 0) {
perror("splice out");
err = 4;
} else if (in_bytes != out_bytes) {
fprintf(stderr, "Unexpected transfer: %zu vs %zu\n",
in_bytes, out_bytes);
err = 5;
} else {
goto again;
}
}
close(pipefd[0]);
close(pipefd[1]);
return err;
}
static int copyfd_io_splice(int infd, int peerfd, int outfd, unsigned int size,
bool *in_closed_after_out, struct wstate *winfo)
{
int err;
if (listen_mode) {
err = do_splice(peerfd, outfd, size, winfo);
if (err)
return err;
err = do_splice(infd, peerfd, size, winfo);
} else {
err = do_splice(infd, peerfd, size, winfo);
if (err)
return err;
shut_wr(peerfd);
err = do_splice(peerfd, outfd, size, winfo);
*in_closed_after_out = true;
}
return err;
}
static int copyfd_io(int infd, int peerfd, int outfd, bool close_peerfd, struct wstate *winfo)
{
bool in_closed_after_out = false;
@ -967,6 +1033,14 @@ static int copyfd_io(int infd, int peerfd, int outfd, bool close_peerfd, struct
&in_closed_after_out, winfo);
break;
case CFG_MODE_SPLICE:
file_size = get_infd_size(infd);
if (file_size < 0)
return file_size;
ret = copyfd_io_splice(infd, peerfd, outfd, file_size,
&in_closed_after_out, winfo);
break;
default:
fprintf(stderr, "Invalid mode %d\n", cfg_mode);
@ -1380,12 +1454,15 @@ int parse_mode(const char *mode)
return CFG_MODE_MMAP;
if (!strcasecmp(mode, "sendfile"))
return CFG_MODE_SENDFILE;
if (!strcasecmp(mode, "splice"))
return CFG_MODE_SPLICE;
fprintf(stderr, "Unknown test mode: %s\n", mode);
fprintf(stderr, "Supported modes are:\n");
fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n");
fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n");
fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n");
fprintf(stderr, "\t\t\"splice\" - send entire input file (splice), then read response (-l will read input first)\n");
die_usage();