From: Geliang Tang <[email protected]>

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 <[email protected]>
Reviewed-by: Mat Martineau <[email protected]>
Co-developed-by: Matthieu Baerts (NGI0) <[email protected]>
Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>
---
v2: catch possible errors with splice() (NIPA review assistant)
---
 tools/testing/selftests/net/mptcp/mptcp_connect.c | 79 ++++++++++++++++++++++-
 1 file changed, 78 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c 
b/tools/testing/selftests/net/mptcp/mptcp_connect.c
index 10f6f99cfd4e..a74b13e42ecd 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
@@ -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();
 

-- 
2.51.0


Reply via email to