mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
generic: test splice() with pipes
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>
This commit is contained in:
committed by
Eryu Guan
parent
8c9705b798
commit
568920df38
@@ -113,6 +113,7 @@
|
||||
/src/runas
|
||||
/src/seek_copy_test
|
||||
/src/seek_sanity_test
|
||||
/src/splice-test
|
||||
/src/stale_handle
|
||||
/src/stat_test
|
||||
/src/swapon
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Executable
+42
@@ -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
|
||||
@@ -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
|
||||
@@ -593,3 +593,4 @@
|
||||
588 auto quick log clone
|
||||
589 auto mount
|
||||
590 auto prealloc preallocrw
|
||||
591 auto quick rw pipe splice
|
||||
|
||||
Reference in New Issue
Block a user