[RFC] generic: test splice() with pipes
diff mbox series

Message ID 20190830004407.GA5340@magnolia
State New
Headers show
Series
  • [RFC] generic: test splice() with pipes
Related show

Commit Message

Darrick J. Wong Aug. 30, 2019, 12:44 a.m. UTC
From: Darrick J. Wong <darrick.wong@oracle.com>

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.

XXX Andreas: Since you wrote the C reproducer, can you send me the
proper copyright and author attribution statement for the C program?

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 .gitignore            |    1 
 src/Makefile          |    2 -
 src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/720     |   41 ++++++++++++
 tests/generic/720.out |    7 ++
 tests/generic/group   |    1 
 6 files changed, 224 insertions(+), 1 deletion(-)
 create mode 100644 src/splice-test.c
 create mode 100755 tests/generic/720
 create mode 100644 tests/generic/720.out

Comments

Zorro Lang Sept. 2, 2019, 2:20 a.m. UTC | #1
On Thu, Aug 29, 2019 at 05:44:07PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> 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.
> 
> XXX Andreas: Since you wrote the C reproducer, can you send me the
> proper copyright and author attribution statement for the C program?
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---

Hi Darrick,

I tried to add a splice_f operation into xfs_io long time ago:
https://marc.info/?l=linux-xfs&m=155828702128047&w=2

For we can easily write more splice related test case later, I'd like to have
a xfs_io to help that (if it can). Would you like to help to review that patch,
if it can match your requirement to write this case? Or it can be improved to
cover more testing conditions? I'm really appreciate that:)

Thanks,
Zorro

>  .gitignore            |    1 
>  src/Makefile          |    2 -
>  src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/720     |   41 ++++++++++++
>  tests/generic/720.out |    7 ++
>  tests/generic/group   |    1 
>  6 files changed, 224 insertions(+), 1 deletion(-)
>  create mode 100644 src/splice-test.c
>  create mode 100755 tests/generic/720
>  create mode 100644 tests/generic/720.out
> 
> diff --git a/.gitignore b/.gitignore
> index c8c815f9..26d4da11 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -112,6 +112,7 @@
>  /src/runas
>  /src/seek_copy_test
>  /src/seek_sanity_test
> +/src/splice-test
>  /src/stale_handle
>  /src/stat_test
>  /src/swapon
> diff --git a/src/Makefile b/src/Makefile
> index c4fcf370..2920dfb1 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -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
> index 00000000..d3c12075
> --- /dev/null
> +++ b/src/splice-test.c
> @@ -0,0 +1,173 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2019 ?????????????????????????????
> + * Author: 
> + *
> + * 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/720 b/tests/generic/720
> new file mode 100755
> index 00000000..b7f09c40
> --- /dev/null
> +++ b/tests/generic/720
> @@ -0,0 +1,41 @@
> +#! /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. 720
> +#
> +# 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
> +
> +rm -f $seqres.full
> +
> +src/splice-test -r $TEST_DIR/a
> +src/splice-test -rd $TEST_DIR/a
> +src/splice-test $TEST_DIR/a
> +src/splice-test -d $TEST_DIR/a
> +
> +# success, all done
> +status=0
> +exit
> diff --git a/tests/generic/720.out b/tests/generic/720.out
> new file mode 100644
> index 00000000..b0fc9935
> --- /dev/null
> +++ b/tests/generic/720.out
> @@ -0,0 +1,7 @@
> +QA output created by 720
> +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
> diff --git a/tests/generic/group b/tests/generic/group
> index cd418106..f75d4e60 100644
> --- a/tests/generic/group
> +++ b/tests/generic/group
> @@ -569,3 +569,4 @@
>  564 auto quick copy_range
>  565 auto quick copy_range
>  719 auto quick quota metadata
> +720 auto quick rw pipe splice
Darrick J. Wong Sept. 2, 2019, 4:56 p.m. UTC | #2
On Mon, Sep 02, 2019 at 10:20:52AM +0800, Zorro Lang wrote:
> On Thu, Aug 29, 2019 at 05:44:07PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > 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.
> > 
> > XXX Andreas: Since you wrote the C reproducer, can you send me the
> > proper copyright and author attribution statement for the C program?
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> 
> Hi Darrick,
> 
> I tried to add a splice_f operation into xfs_io long time ago:
> https://marc.info/?l=linux-xfs&m=155828702128047&w=2
> 
> For we can easily write more splice related test case later, I'd like to have
> a xfs_io to help that (if it can). Would you like to help to review that patch,
> if it can match your requirement to write this case? Or it can be improved to
> cover more testing conditions? I'm really appreciate that:)

Yeah, I'll try to have a look this week.

(FWIW I think Eric is backed up with dozens of xfsprogs patches...)

--D

> Thanks,
> Zorro
> 
> >  .gitignore            |    1 
> >  src/Makefile          |    2 -
> >  src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  tests/generic/720     |   41 ++++++++++++
> >  tests/generic/720.out |    7 ++
> >  tests/generic/group   |    1 
> >  6 files changed, 224 insertions(+), 1 deletion(-)
> >  create mode 100644 src/splice-test.c
> >  create mode 100755 tests/generic/720
> >  create mode 100644 tests/generic/720.out
> > 
> > diff --git a/.gitignore b/.gitignore
> > index c8c815f9..26d4da11 100644
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -112,6 +112,7 @@
> >  /src/runas
> >  /src/seek_copy_test
> >  /src/seek_sanity_test
> > +/src/splice-test
> >  /src/stale_handle
> >  /src/stat_test
> >  /src/swapon
> > diff --git a/src/Makefile b/src/Makefile
> > index c4fcf370..2920dfb1 100644
> > --- a/src/Makefile
> > +++ b/src/Makefile
> > @@ -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
> > index 00000000..d3c12075
> > --- /dev/null
> > +++ b/src/splice-test.c
> > @@ -0,0 +1,173 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2019 ?????????????????????????????
> > + * Author: 
> > + *
> > + * 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/720 b/tests/generic/720
> > new file mode 100755
> > index 00000000..b7f09c40
> > --- /dev/null
> > +++ b/tests/generic/720
> > @@ -0,0 +1,41 @@
> > +#! /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. 720
> > +#
> > +# 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
> > +
> > +rm -f $seqres.full
> > +
> > +src/splice-test -r $TEST_DIR/a
> > +src/splice-test -rd $TEST_DIR/a
> > +src/splice-test $TEST_DIR/a
> > +src/splice-test -d $TEST_DIR/a
> > +
> > +# success, all done
> > +status=0
> > +exit
> > diff --git a/tests/generic/720.out b/tests/generic/720.out
> > new file mode 100644
> > index 00000000..b0fc9935
> > --- /dev/null
> > +++ b/tests/generic/720.out
> > @@ -0,0 +1,7 @@
> > +QA output created by 720
> > +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
> > diff --git a/tests/generic/group b/tests/generic/group
> > index cd418106..f75d4e60 100644
> > --- a/tests/generic/group
> > +++ b/tests/generic/group
> > @@ -569,3 +569,4 @@
> >  564 auto quick copy_range
> >  565 auto quick copy_range
> >  719 auto quick quota metadata
> > +720 auto quick rw pipe splice
Zorro Lang Sept. 3, 2019, 3:19 a.m. UTC | #3
On Mon, Sep 02, 2019 at 09:56:07AM -0700, Darrick J. Wong wrote:
> On Mon, Sep 02, 2019 at 10:20:52AM +0800, Zorro Lang wrote:
> > On Thu, Aug 29, 2019 at 05:44:07PM -0700, Darrick J. Wong wrote:
> > > From: Darrick J. Wong <darrick.wong@oracle.com>
> > > 
> > > 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.
> > > 
> > > XXX Andreas: Since you wrote the C reproducer, can you send me the
> > > proper copyright and author attribution statement for the C program?
> > > 
> > > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > ---
> > 
> > Hi Darrick,
> > 
> > I tried to add a splice_f operation into xfs_io long time ago:
> > https://marc.info/?l=linux-xfs&m=155828702128047&w=2
> > 
> > For we can easily write more splice related test case later, I'd like to have
> > a xfs_io to help that (if it can). Would you like to help to review that patch,
> > if it can match your requirement to write this case? Or it can be improved to
> > cover more testing conditions? I'm really appreciate that:)
> 
> Yeah, I'll try to have a look this week.

Thanks so much, I wrote this patch with 'fsstress supports splice' together,
when we found a XFS regression bug by splice operation. But this xfs_io command
doesn't have a specified usage to reproduce a bug. So if it can be improved to
be used to cover the case which you just wrote (about splice), that might be
better I think.

Thanks,
Zorro

> 
> (FWIW I think Eric is backed up with dozens of xfsprogs patches...)
> 
> --D
> 
> > Thanks,
> > Zorro
> > 
> > >  .gitignore            |    1 
> > >  src/Makefile          |    2 -
> > >  src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
> > >  tests/generic/720     |   41 ++++++++++++
> > >  tests/generic/720.out |    7 ++
> > >  tests/generic/group   |    1 
> > >  6 files changed, 224 insertions(+), 1 deletion(-)
> > >  create mode 100644 src/splice-test.c
> > >  create mode 100755 tests/generic/720
> > >  create mode 100644 tests/generic/720.out
> > > 
> > > diff --git a/.gitignore b/.gitignore
> > > index c8c815f9..26d4da11 100644
> > > --- a/.gitignore
> > > +++ b/.gitignore
> > > @@ -112,6 +112,7 @@
> > >  /src/runas
> > >  /src/seek_copy_test
> > >  /src/seek_sanity_test
> > > +/src/splice-test
> > >  /src/stale_handle
> > >  /src/stat_test
> > >  /src/swapon
> > > diff --git a/src/Makefile b/src/Makefile
> > > index c4fcf370..2920dfb1 100644
> > > --- a/src/Makefile
> > > +++ b/src/Makefile
> > > @@ -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
> > > index 00000000..d3c12075
> > > --- /dev/null
> > > +++ b/src/splice-test.c
> > > @@ -0,0 +1,173 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > +/*
> > > + * Copyright (C) 2019 ?????????????????????????????
> > > + * Author: 
> > > + *
> > > + * 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/720 b/tests/generic/720
> > > new file mode 100755
> > > index 00000000..b7f09c40
> > > --- /dev/null
> > > +++ b/tests/generic/720
> > > @@ -0,0 +1,41 @@
> > > +#! /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. 720
> > > +#
> > > +# 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
> > > +
> > > +rm -f $seqres.full
> > > +
> > > +src/splice-test -r $TEST_DIR/a
> > > +src/splice-test -rd $TEST_DIR/a
> > > +src/splice-test $TEST_DIR/a
> > > +src/splice-test -d $TEST_DIR/a
> > > +
> > > +# success, all done
> > > +status=0
> > > +exit
> > > diff --git a/tests/generic/720.out b/tests/generic/720.out
> > > new file mode 100644
> > > index 00000000..b0fc9935
> > > --- /dev/null
> > > +++ b/tests/generic/720.out
> > > @@ -0,0 +1,7 @@
> > > +QA output created by 720
> > > +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
> > > diff --git a/tests/generic/group b/tests/generic/group
> > > index cd418106..f75d4e60 100644
> > > --- a/tests/generic/group
> > > +++ b/tests/generic/group
> > > @@ -569,3 +569,4 @@
> > >  564 auto quick copy_range
> > >  565 auto quick copy_range
> > >  719 auto quick quota metadata
> > > +720 auto quick rw pipe splice
Darrick J. Wong Nov. 21, 2019, 5:01 p.m. UTC | #4
On Thu, Aug 29, 2019 at 05:44:07PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> 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.
> 
> XXX Andreas: Since you wrote the C reproducer, can you send me the
> proper copyright and author attribution statement for the C program?

Ping?  Andreas, can I get the above info so I can get this moving again?

--D

> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  .gitignore            |    1 
>  src/Makefile          |    2 -
>  src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/720     |   41 ++++++++++++
>  tests/generic/720.out |    7 ++
>  tests/generic/group   |    1 
>  6 files changed, 224 insertions(+), 1 deletion(-)
>  create mode 100644 src/splice-test.c
>  create mode 100755 tests/generic/720
>  create mode 100644 tests/generic/720.out
> 
> diff --git a/.gitignore b/.gitignore
> index c8c815f9..26d4da11 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -112,6 +112,7 @@
>  /src/runas
>  /src/seek_copy_test
>  /src/seek_sanity_test
> +/src/splice-test
>  /src/stale_handle
>  /src/stat_test
>  /src/swapon
> diff --git a/src/Makefile b/src/Makefile
> index c4fcf370..2920dfb1 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -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
> index 00000000..d3c12075
> --- /dev/null
> +++ b/src/splice-test.c
> @@ -0,0 +1,173 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2019 ?????????????????????????????
> + * Author: 
> + *
> + * 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/720 b/tests/generic/720
> new file mode 100755
> index 00000000..b7f09c40
> --- /dev/null
> +++ b/tests/generic/720
> @@ -0,0 +1,41 @@
> +#! /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. 720
> +#
> +# 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
> +
> +rm -f $seqres.full
> +
> +src/splice-test -r $TEST_DIR/a
> +src/splice-test -rd $TEST_DIR/a
> +src/splice-test $TEST_DIR/a
> +src/splice-test -d $TEST_DIR/a
> +
> +# success, all done
> +status=0
> +exit
> diff --git a/tests/generic/720.out b/tests/generic/720.out
> new file mode 100644
> index 00000000..b0fc9935
> --- /dev/null
> +++ b/tests/generic/720.out
> @@ -0,0 +1,7 @@
> +QA output created by 720
> +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
> diff --git a/tests/generic/group b/tests/generic/group
> index cd418106..f75d4e60 100644
> --- a/tests/generic/group
> +++ b/tests/generic/group
> @@ -569,3 +569,4 @@
>  564 auto quick copy_range
>  565 auto quick copy_range
>  719 auto quick quota metadata
> +720 auto quick rw pipe splice
Andreas Grünbacher Nov. 21, 2019, 6:48 p.m. UTC | #5
Am Do., 21. Nov. 2019 um 18:01 Uhr schrieb Darrick J. Wong
<darrick.wong@oracle.com>:
> On Thu, Aug 29, 2019 at 05:44:07PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> >
> > 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.
> >
> > XXX Andreas: Since you wrote the C reproducer, can you send me the
> > proper copyright and author attribution statement for the C program?
>
> Ping?  Andreas, can I get the above info so I can get this moving again?

Oops, sure, this is:

Copyright (c) 2019 RedHat Inc.  All Rights Reserved.
Author: Andreas Gruenbacher <agruenba@redhat.com>

> --D
>
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  .gitignore            |    1
> >  src/Makefile          |    2 -
> >  src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  tests/generic/720     |   41 ++++++++++++
> >  tests/generic/720.out |    7 ++
> >  tests/generic/group   |    1
> >  6 files changed, 224 insertions(+), 1 deletion(-)
> >  create mode 100644 src/splice-test.c
> >  create mode 100755 tests/generic/720
> >  create mode 100644 tests/generic/720.out
> >
> > diff --git a/.gitignore b/.gitignore
> > index c8c815f9..26d4da11 100644
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -112,6 +112,7 @@
> >  /src/runas
> >  /src/seek_copy_test
> >  /src/seek_sanity_test
> > +/src/splice-test
> >  /src/stale_handle
> >  /src/stat_test
> >  /src/swapon
> > diff --git a/src/Makefile b/src/Makefile
> > index c4fcf370..2920dfb1 100644
> > --- a/src/Makefile
> > +++ b/src/Makefile
> > @@ -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
> > index 00000000..d3c12075
> > --- /dev/null
> > +++ b/src/splice-test.c
> > @@ -0,0 +1,173 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2019 ????????????????????????????
> > + * Author:
> > + *
> > + * 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/720 b/tests/generic/720
> > new file mode 100755
> > index 00000000..b7f09c40
> > --- /dev/null
> > +++ b/tests/generic/720
> > @@ -0,0 +1,41 @@
> > +#! /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. 720
> > +#
> > +# 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
> > +
> > +rm -f $seqres.full
> > +
> > +src/splice-test -r $TEST_DIR/a
> > +src/splice-test -rd $TEST_DIR/a
> > +src/splice-test $TEST_DIR/a
> > +src/splice-test -d $TEST_DIR/a
> > +
> > +# success, all done
> > +status=0
> > +exit
> > diff --git a/tests/generic/720.out b/tests/generic/720.out
> > new file mode 100644
> > index 00000000..b0fc9935
> > --- /dev/null
> > +++ b/tests/generic/720.out
> > @@ -0,0 +1,7 @@
> > +QA output created by 720
> > +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
> > diff --git a/tests/generic/group b/tests/generic/group
> > index cd418106..f75d4e60 100644
> > --- a/tests/generic/group
> > +++ b/tests/generic/group
> > @@ -569,3 +569,4 @@
> >  564 auto quick copy_range
> >  565 auto quick copy_range
> >  719 auto quick quota metadata
> > +720 auto quick rw pipe splice

Thanks,
Andreas
Darrick J. Wong Nov. 21, 2019, 7:14 p.m. UTC | #6
On Thu, Nov 21, 2019 at 07:48:54PM +0100, Andreas Grünbacher wrote:
> Am Do., 21. Nov. 2019 um 18:01 Uhr schrieb Darrick J. Wong
> <darrick.wong@oracle.com>:
> > On Thu, Aug 29, 2019 at 05:44:07PM -0700, Darrick J. Wong wrote:
> > > From: Darrick J. Wong <darrick.wong@oracle.com>
> > >
> > > 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.
> > >
> > > XXX Andreas: Since you wrote the C reproducer, can you send me the
> > > proper copyright and author attribution statement for the C program?
> >
> > Ping?  Andreas, can I get the above info so I can get this moving again?
> 
> Oops, sure, this is:
> 
> Copyright (c) 2019 RedHat Inc.  All Rights Reserved.
> Author: Andreas Gruenbacher <agruenba@redhat.com>

Ok thanks.  It's appropriate to tag it as GPL v2 licensed, correct?

--D

> > --D
> >
> > > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > ---
> > >  .gitignore            |    1
> > >  src/Makefile          |    2 -
> > >  src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
> > >  tests/generic/720     |   41 ++++++++++++
> > >  tests/generic/720.out |    7 ++
> > >  tests/generic/group   |    1
> > >  6 files changed, 224 insertions(+), 1 deletion(-)
> > >  create mode 100644 src/splice-test.c
> > >  create mode 100755 tests/generic/720
> > >  create mode 100644 tests/generic/720.out
> > >
> > > diff --git a/.gitignore b/.gitignore
> > > index c8c815f9..26d4da11 100644
> > > --- a/.gitignore
> > > +++ b/.gitignore
> > > @@ -112,6 +112,7 @@
> > >  /src/runas
> > >  /src/seek_copy_test
> > >  /src/seek_sanity_test
> > > +/src/splice-test
> > >  /src/stale_handle
> > >  /src/stat_test
> > >  /src/swapon
> > > diff --git a/src/Makefile b/src/Makefile
> > > index c4fcf370..2920dfb1 100644
> > > --- a/src/Makefile
> > > +++ b/src/Makefile
> > > @@ -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
> > > index 00000000..d3c12075
> > > --- /dev/null
> > > +++ b/src/splice-test.c
> > > @@ -0,0 +1,173 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > +/*
> > > + * Copyright (C) 2019 ????????????????????????????
> > > + * Author:
> > > + *
> > > + * 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/720 b/tests/generic/720
> > > new file mode 100755
> > > index 00000000..b7f09c40
> > > --- /dev/null
> > > +++ b/tests/generic/720
> > > @@ -0,0 +1,41 @@
> > > +#! /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. 720
> > > +#
> > > +# 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
> > > +
> > > +rm -f $seqres.full
> > > +
> > > +src/splice-test -r $TEST_DIR/a
> > > +src/splice-test -rd $TEST_DIR/a
> > > +src/splice-test $TEST_DIR/a
> > > +src/splice-test -d $TEST_DIR/a
> > > +
> > > +# success, all done
> > > +status=0
> > > +exit
> > > diff --git a/tests/generic/720.out b/tests/generic/720.out
> > > new file mode 100644
> > > index 00000000..b0fc9935
> > > --- /dev/null
> > > +++ b/tests/generic/720.out
> > > @@ -0,0 +1,7 @@
> > > +QA output created by 720
> > > +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
> > > diff --git a/tests/generic/group b/tests/generic/group
> > > index cd418106..f75d4e60 100644
> > > --- a/tests/generic/group
> > > +++ b/tests/generic/group
> > > @@ -569,3 +569,4 @@
> > >  564 auto quick copy_range
> > >  565 auto quick copy_range
> > >  719 auto quick quota metadata
> > > +720 auto quick rw pipe splice
> 
> Thanks,
> Andreas
Andreas Grünbacher Nov. 22, 2019, 1:27 a.m. UTC | #7
Am Do., 21. Nov. 2019 um 20:14 Uhr schrieb Darrick J. Wong
<darrick.wong@oracle.com>:
> On Thu, Nov 21, 2019 at 07:48:54PM +0100, Andreas Grünbacher wrote:
> > Am Do., 21. Nov. 2019 um 18:01 Uhr schrieb Darrick J. Wong
> > <darrick.wong@oracle.com>:
> > > On Thu, Aug 29, 2019 at 05:44:07PM -0700, Darrick J. Wong wrote:
> > > > From: Darrick J. Wong <darrick.wong@oracle.com>
> > > >
> > > > 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.
> > > >
> > > > XXX Andreas: Since you wrote the C reproducer, can you send me the
> > > > proper copyright and author attribution statement for the C program?
> > >
> > > Ping?  Andreas, can I get the above info so I can get this moving again?
> >
> > Oops, sure, this is:
> >
> > Copyright (c) 2019 RedHat Inc.  All Rights Reserved.
> > Author: Andreas Gruenbacher <agruenba@redhat.com>
>
> Ok thanks.  It's appropriate to tag it as GPL v2 licensed, correct?

My prevous reply didn't quite make it, so again, yes, that's correct.

Thanks,
Andreas

> --D
>
> > > --D
> > >
> > > > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > > ---
> > > >  .gitignore            |    1
> > > >  src/Makefile          |    2 -
> > > >  src/splice-test.c     |  173 +++++++++++++++++++++++++++++++++++++++++++++++++
> > > >  tests/generic/720     |   41 ++++++++++++
> > > >  tests/generic/720.out |    7 ++
> > > >  tests/generic/group   |    1
> > > >  6 files changed, 224 insertions(+), 1 deletion(-)
> > > >  create mode 100644 src/splice-test.c
> > > >  create mode 100755 tests/generic/720
> > > >  create mode 100644 tests/generic/720.out
> > > >
> > > > diff --git a/.gitignore b/.gitignore
> > > > index c8c815f9..26d4da11 100644
> > > > --- a/.gitignore
> > > > +++ b/.gitignore
> > > > @@ -112,6 +112,7 @@
> > > >  /src/runas
> > > >  /src/seek_copy_test
> > > >  /src/seek_sanity_test
> > > > +/src/splice-test
> > > >  /src/stale_handle
> > > >  /src/stat_test
> > > >  /src/swapon
> > > > diff --git a/src/Makefile b/src/Makefile
> > > > index c4fcf370..2920dfb1 100644
> > > > --- a/src/Makefile
> > > > +++ b/src/Makefile
> > > > @@ -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
> > > > index 00000000..d3c12075
> > > > --- /dev/null
> > > > +++ b/src/splice-test.c
> > > > @@ -0,0 +1,173 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > +/*
> > > > + * Copyright (C) 2019 ????????????????????????????
> > > > + * Author:
> > > > + *
> > > > + * 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/720 b/tests/generic/720
> > > > new file mode 100755
> > > > index 00000000..b7f09c40
> > > > --- /dev/null
> > > > +++ b/tests/generic/720
> > > > @@ -0,0 +1,41 @@
> > > > +#! /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. 720
> > > > +#
> > > > +# 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
> > > > +
> > > > +rm -f $seqres.full
> > > > +
> > > > +src/splice-test -r $TEST_DIR/a
> > > > +src/splice-test -rd $TEST_DIR/a
> > > > +src/splice-test $TEST_DIR/a
> > > > +src/splice-test -d $TEST_DIR/a
> > > > +
> > > > +# success, all done
> > > > +status=0
> > > > +exit
> > > > diff --git a/tests/generic/720.out b/tests/generic/720.out
> > > > new file mode 100644
> > > > index 00000000..b0fc9935
> > > > --- /dev/null
> > > > +++ b/tests/generic/720.out
> > > > @@ -0,0 +1,7 @@
> > > > +QA output created by 720
> > > > +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
> > > > diff --git a/tests/generic/group b/tests/generic/group
> > > > index cd418106..f75d4e60 100644
> > > > --- a/tests/generic/group
> > > > +++ b/tests/generic/group
> > > > @@ -569,3 +569,4 @@
> > > >  564 auto quick copy_range
> > > >  565 auto quick copy_range
> > > >  719 auto quick quota metadata
> > > > +720 auto quick rw pipe splice
> >
> > Thanks,
> > Andreas

Patch
diff mbox series

diff --git a/.gitignore b/.gitignore
index c8c815f9..26d4da11 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,6 +112,7 @@ 
 /src/runas
 /src/seek_copy_test
 /src/seek_sanity_test
+/src/splice-test
 /src/stale_handle
 /src/stat_test
 /src/swapon
diff --git a/src/Makefile b/src/Makefile
index c4fcf370..2920dfb1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
index 00000000..d3c12075
--- /dev/null
+++ b/src/splice-test.c
@@ -0,0 +1,173 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 ?????????????????????????????
+ * Author: 
+ *
+ * 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/720 b/tests/generic/720
new file mode 100755
index 00000000..b7f09c40
--- /dev/null
+++ b/tests/generic/720
@@ -0,0 +1,41 @@ 
+#! /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. 720
+#
+# 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
+
+rm -f $seqres.full
+
+src/splice-test -r $TEST_DIR/a
+src/splice-test -rd $TEST_DIR/a
+src/splice-test $TEST_DIR/a
+src/splice-test -d $TEST_DIR/a
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/720.out b/tests/generic/720.out
new file mode 100644
index 00000000..b0fc9935
--- /dev/null
+++ b/tests/generic/720.out
@@ -0,0 +1,7 @@ 
+QA output created by 720
+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
diff --git a/tests/generic/group b/tests/generic/group
index cd418106..f75d4e60 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -569,3 +569,4 @@ 
 564 auto quick copy_range
 565 auto quick copy_range
 719 auto quick quota metadata
+720 auto quick rw pipe splice