diff options
author | Filipe Manana <fdmanana@suse.com> | 2022-03-03 11:36:42 +0000 |
---|---|---|
committer | Eryu Guan <guaneryu@gmail.com> | 2022-04-10 20:25:01 +0800 |
commit | cce62cbb35cdfab197ce7297c2997c3d460aaab2 (patch) | |
tree | bc2fac8cb4fa53608c8a12de6abc138932c12949 | |
parent | 4837b6aff4d3e466d3dfc3120fe4568ecf717180 (diff) | |
download | xfstests-dev-cce62cbb35cdfab197ce7297c2997c3d460aaab2.tar.gz |
generic: read multiple extents with io_uring
Test doing a read, with io_uring, over a file range that includes
multiple extents. The read operation triggers page faults when
accessing all pages of the read buffer except for the pages
corresponding to the first extent. We want to check that the
operation results in reading all the extents and that it returns the
correct data.
This is motivated by an issue found with MariaDB when using io_uring
and running on btrfs. There's a patch for btrfs to address the issue
ca93e44bfb5f ("btrfs: fallback to blocking mode when doing async dio
over multiple extents")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Eryu Guan <guaneryu@gmail.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/Makefile | 1 | ||||
-rw-r--r-- | src/uring_read_fault.c | 150 | ||||
-rwxr-xr-x | tests/generic/678 | 40 | ||||
-rw-r--r-- | tests/generic/678.out | 2 |
5 files changed, 194 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore index ca6ba5c473..11405bd2f5 100644 --- a/.gitignore +++ b/.gitignore @@ -169,6 +169,7 @@ tags /src/truncfile /src/unwritten_mmap /src/unwritten_sync +/src/uring_read_fault /src/usemem /src/writemod /src/writev_on_pagefault diff --git a/src/Makefile b/src/Makefile index 4d9e02b74d..88877ac9de 100644 --- a/src/Makefile +++ b/src/Makefile @@ -72,6 +72,7 @@ LLDLIBS += -laio endif ifeq ($(HAVE_URING), true) +TARGETS += uring_read_fault LLDLIBS += -luring endif diff --git a/src/uring_read_fault.c b/src/uring_read_fault.c new file mode 100644 index 0000000000..9fb86c47b8 --- /dev/null +++ b/src/uring_read_fault.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 SUSE Linux Products GmbH. All Rights Reserved. + */ + +/* + * Create a file with 4 extents, each with a size matching the page size. + * Then allocate a buffer to read all extents with io_uring, using O_DIRECT and + * IOCB_NOWAIT. Before doing the read with io_uring, access the first page of + * the read buffer to fault it in, so that during the read we only trigger page + * faults when accessing the other pages (mapping to 2nd, 3rd and 4th extents). + */ + +/* Get the O_DIRECT definition. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <liburing.h> + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct iovec iovec; + int fd; + long pagesize; + void *write_buf; + void *read_buf; + ssize_t ret; + int i; + + if (argc != 2) { + fprintf(stderr, "Use: %s <file path>\n", argv[0]); + return 1; + } + + fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY | O_DIRECT, 0666); + if (fd == -1) { + fprintf(stderr, "Failed to create file %s: %s (errno %d)\n", + argv[1], strerror(errno), errno); + return 1; + } + + pagesize = sysconf(_SC_PAGE_SIZE); + if (pagesize == -1) { + fprintf(stderr, "Failed to get page size: %s (errno %d)\n", + strerror(errno), errno); + return 1; + } + + ret = posix_memalign(&write_buf, pagesize, 4 * pagesize); + if (ret) { + fprintf(stderr, "Failed to allocate write buffer\n"); + return 1; + } + + memset(write_buf, 0xab, pagesize); + memset(write_buf + pagesize, 0xcd, pagesize); + memset(write_buf + 2 * pagesize, 0xef, pagesize); + memset(write_buf + 3 * pagesize, 0x73, pagesize); + + /* Create the 4 extents, each with a size matching page size. */ + for (i = 0; i < 4; i++) { + ret = pwrite(fd, write_buf + i * pagesize, pagesize, + i * pagesize); + if (ret != pagesize) { + fprintf(stderr, + "Write failure, ret = %ld errno %d (%s)\n", + ret, errno, strerror(errno)); + return 1; + } + ret = fsync(fd); + if (ret != 0) { + fprintf(stderr, "Fsync failure: %s (errno %d)\n", + strerror(errno), errno); + return 1; + } + } + + close(fd); + fd = open(argv[1], O_RDONLY | O_DIRECT); + if (fd == -1) { + fprintf(stderr, + "Failed to open file %s: %s (errno %d)", + argv[1], strerror(errno), errno); + return 1; + } + + ret = posix_memalign(&read_buf, pagesize, 4 * pagesize); + if (ret) { + fprintf(stderr, "Failed to allocate read buffer\n"); + return 1; + } + + /* + * Fault in only the first page of the read buffer. + * We want to trigger a page fault for the 2nd page of the read buffer + * during the read operation with io_uring (O_DIRECT and IOCB_NOWAIT). + */ + memset(read_buf, 0, 1); + + ret = io_uring_queue_init(1, &ring, 0); + if (ret != 0) { + fprintf(stderr, "Failed to creating io_uring queue\n"); + return 1; + } + + sqe = io_uring_get_sqe(&ring); + if (!sqe) { + fprintf(stderr, "Failed to get io_uring sqe\n"); + return 1; + } + + iovec.iov_base = read_buf; + iovec.iov_len = 4 * pagesize; + io_uring_prep_readv(sqe, fd, &iovec, 1, 0); + + ret = io_uring_submit_and_wait(&ring, 1); + if (ret != 1) { + fprintf(stderr, "Failed at io_uring_submit_and_wait()\n"); + return 1; + } + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret < 0) { + fprintf(stderr, "Failed at io_uring_wait_cqe()\n"); + return 1; + } + + if (cqe->res != (4 * pagesize)) + fprintf(stderr, "Unexpected cqe->res, got %d expected %ld\n", + cqe->res, 4 * pagesize); + + if (memcmp(read_buf, write_buf, 4 * pagesize) != 0) + fprintf(stderr, + "Unexpected mismatch between read and write buffers\n"); + + io_uring_cqe_seen(&ring, cqe); + io_uring_queue_exit(&ring); + + return 0; +} diff --git a/tests/generic/678 b/tests/generic/678 new file mode 100755 index 0000000000..1c3cf6e3a8 --- /dev/null +++ b/tests/generic/678 @@ -0,0 +1,40 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2022 SUSE Linux Products GmbH. All Rights Reserved. +# +# FS QA Test 678 +# +# Test doing a read, with io_uring, over a file range that includes multiple +# extents. The read operation triggers page faults when accessing all pages of +# the read buffer except for the pages corresponding to the first extent. +# Then verify that the operation results in reading all the extents and returns +# the correct data. +# +. ./common/preamble +_begin_fstest auto quick io_uring + +# Override the default cleanup function. +_cleanup() +{ + cd / + rm -f $tmp.* + rm -f $TEST_DIR/uring_read_fault.tmp +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# real QA test starts here + +_supported_fs generic +_require_test +_require_odirect +_require_io_uring +_require_test_program uring_read_fault + +$here/src/uring_read_fault $TEST_DIR/uring_read_fault.tmp +status=$? + +echo "Silence is golden" +exit diff --git a/tests/generic/678.out b/tests/generic/678.out new file mode 100644 index 0000000000..452c31e628 --- /dev/null +++ b/tests/generic/678.out @@ -0,0 +1,2 @@ +QA output created by 678 +Silence is golden |