generic: test splice() with pipes
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 4 Dec 2019 02:37:24 +0000 (18:37 -0800)
committerEryu Guan <guaneryu@gmail.com>
Sun, 8 Dec 2019 16:09:32 +0000 (00:09 +0800)
Andreas Grünbacher reports that on the two filesystems that support
iomap directio, it's possible for splice() to return -EAGAIN (instead of
a short splice) if the pipe being written to has less space available in
its pipe buffers than the length supplied by the calling process.

This is a regression test to check for correct operation. Kernel
needs commit 3253d9d09337 ("splice: only read in as much information
as there is pipe buffer space") to pass.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eryu Guan <guaneryu@gmail.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
.gitignore
src/Makefile
src/splice-test.c [new file with mode: 0644]
tests/generic/591 [new file with mode: 0755]
tests/generic/591.out [new file with mode: 0644]
tests/generic/group

index b0f96ca01e83f6666ec1b23221ddea7150033bd9..7e6b7daf11907288784dee9bc600084eabc51829 100644 (file)
 /src/runas
 /src/seek_copy_test
 /src/seek_sanity_test
+/src/splice-test
 /src/stale_handle
 /src/stat_test
 /src/swapon
index ce6d86100885e6e42aae281a0029bfea72df8b41..b015e78b5e949305340dbe9823d876eeafa56259 100644 (file)
@@ -28,7 +28,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
        attr-list-by-handle-cursor-test listxattr dio-interleaved t_dir_type \
        dio-invalidate-cache stat_test t_encrypted_d_revalidate \
        attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
-       fscrypt-crypt-util bulkstat_null_ocount
+       fscrypt-crypt-util bulkstat_null_ocount splice-test
 
 SUBDIRS = log-writes perf
 
diff --git a/src/splice-test.c b/src/splice-test.c
new file mode 100644 (file)
index 0000000..11fb11f
--- /dev/null
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 RedHat Inc.  All Rights Reserved.
+ * Author: Andreas Gruenbacher <agruenba@redhat.com>
+ *
+ * Make sure that reading and writing to a pipe via splice.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#define SECTOR_SIZE 512
+#define BUFFER_SIZE (150 * SECTOR_SIZE)
+
+void read_from_pipe(int fd, const char *filename, size_t size)
+{
+       char buffer[SECTOR_SIZE];
+       size_t sz;
+       ssize_t ret;
+
+       while (size) {
+               sz = size;
+               if (sz > sizeof buffer)
+                       sz = sizeof buffer;
+               ret = read(fd, buffer, sz);
+               if (ret < 0)
+                       err(1, "read: %s", filename);
+               if (ret == 0) {
+                       fprintf(stderr, "read: %s: unexpected EOF\n", filename);
+                       exit(1);
+               }
+               size -= sz;
+       }
+}
+
+void do_splice1(int fd, const char *filename, size_t size)
+{
+       bool retried = false;
+       int pipefd[2];
+
+       if (pipe(pipefd) == -1)
+               err(1, "pipe");
+       while (size) {
+               ssize_t spliced;
+
+               spliced = splice(fd, NULL, pipefd[1], NULL, size, SPLICE_F_MOVE);
+               if (spliced == -1) {
+                       if (errno == EAGAIN && !retried) {
+                               retried = true;
+                               fprintf(stderr, "retrying splice\n");
+                               sleep(1);
+                               continue;
+                       }
+                       err(1, "splice");
+               }
+               read_from_pipe(pipefd[0], filename, spliced);
+               size -= spliced;
+       }
+       close(pipefd[0]);
+       close(pipefd[1]);
+}
+
+void do_splice2(int fd, const char *filename, size_t size)
+{
+       bool retried = false;
+       int pipefd[2];
+       int pid;
+
+       if (pipe(pipefd) == -1)
+               err(1, "pipe");
+
+       pid = fork();
+       if (pid == 0) {
+               close(pipefd[1]);
+               read_from_pipe(pipefd[0], filename, size);
+               exit(0);
+       } else {
+               close(pipefd[0]);
+               while (size) {
+                       ssize_t spliced;
+
+                       spliced = splice(fd, NULL, pipefd[1], NULL, size, SPLICE_F_MOVE);
+                       if (spliced == -1) {
+                               if (errno == EAGAIN && !retried) {
+                                       retried = true;
+                                       fprintf(stderr, "retrying splice\n");
+                                       sleep(1);
+                                       continue;
+                               }
+                               err(1, "splice");
+                       }
+                       size -= spliced;
+               }
+               close(pipefd[1]);
+               waitpid(pid, NULL, 0);
+       }
+}
+
+void usage(const char *argv0)
+{
+       fprintf(stderr, "USAGE: %s [-rd] {filename}\n", basename(argv0));
+       exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+       void (*do_splice)(int fd, const char *filename, size_t size);
+       const char *filename;
+       char *buffer;
+       int opt, open_flags, fd;
+       ssize_t ret;
+
+       do_splice = do_splice1;
+       open_flags = O_CREAT | O_TRUNC | O_RDWR | O_DIRECT;
+
+       while ((opt = getopt(argc, argv, "rd")) != -1) {
+               switch(opt) {
+               case 'r':
+                       do_splice = do_splice2;
+                       break;
+               case 'd':
+                       open_flags &= ~O_DIRECT;
+                       break;
+               default:  /* '?' */
+                       usage(argv[0]);
+               }
+       }
+
+       if (optind >= argc)
+               usage(argv[0]);
+       filename = argv[optind];
+
+       printf("%s reader %s O_DIRECT\n",
+                  do_splice == do_splice1 ? "sequential" : "concurrent",
+                  (open_flags & O_DIRECT) ? "with" : "without");
+
+       buffer = aligned_alloc(SECTOR_SIZE, BUFFER_SIZE);
+       if (buffer == NULL)
+               err(1, "aligned_alloc");
+
+       fd = open(filename, open_flags, 0666);
+       if (fd == -1)
+               err(1, "open: %s", filename);
+
+       memset(buffer, 'x', BUFFER_SIZE);
+       ret = write(fd, buffer, BUFFER_SIZE);
+       if (ret < 0)
+               err(1, "write: %s", filename);
+       if (ret != BUFFER_SIZE) {
+               fprintf(stderr, "%s: short write\n", filename);
+               exit(1);
+       }
+
+       ret = lseek(fd, 0, SEEK_SET);
+       if (ret != 0)
+               err(1, "lseek: %s", filename);
+
+       do_splice(fd, filename, BUFFER_SIZE);
+
+       if (unlink(filename) == -1)
+               err(1, "unlink: %s", filename);
+
+       return 0;
+}
diff --git a/tests/generic/591 b/tests/generic/591
new file mode 100755 (executable)
index 0000000..6dbc7f1
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2019, Oracle and/or its affiliates.  All Rights Reserved.
+#
+# FS QA Test No. 591
+#
+# Test using splice() to read from pipes.
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1    # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+       cd /
+       rm -f $TEST_DIR/a
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+
+# real QA test starts here
+_supported_os Linux
+_supported_fs generic
+_require_test
+_require_test_program "splice-test"
+
+rm -f $seqres.full
+
+$here/src/splice-test -r $TEST_DIR/a
+$here/src/splice-test -rd $TEST_DIR/a
+$here/src/splice-test $TEST_DIR/a
+$here/src/splice-test -d $TEST_DIR/a
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/591.out b/tests/generic/591.out
new file mode 100644 (file)
index 0000000..d61811e
--- /dev/null
@@ -0,0 +1,7 @@
+QA output created by 591
+concurrent reader with O_DIRECT
+concurrent reader with O_DIRECT
+concurrent reader without O_DIRECT
+concurrent reader without O_DIRECT
+sequential reader with O_DIRECT
+sequential reader without O_DIRECT
index 4f38f652c55fce04cd9d0c7f32bf720b7f214208..90da26c5b42c833bad171cf4294dbf4b878deac7 100644 (file)
 588 auto quick log clone
 589 auto mount
 590 auto prealloc preallocrw
+591 auto quick rw pipe splice