aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2022-03-03 11:36:42 +0000
committerEryu Guan <guaneryu@gmail.com>2022-04-10 20:25:01 +0800
commitcce62cbb35cdfab197ce7297c2997c3d460aaab2 (patch)
treebc2fac8cb4fa53608c8a12de6abc138932c12949
parent4837b6aff4d3e466d3dfc3120fe4568ecf717180 (diff)
downloadxfstests-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--.gitignore1
-rw-r--r--src/Makefile1
-rw-r--r--src/uring_read_fault.c150
-rwxr-xr-xtests/generic/67840
-rw-r--r--tests/generic/678.out2
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