]> www.infradead.org Git - users/hch/xfstests-dev.git/commitdiff
generic: read multiple extents with io_uring
authorFilipe Manana <fdmanana@suse.com>
Thu, 3 Mar 2022 11:36:42 +0000 (11:36 +0000)
committerEryu Guan <guaneryu@gmail.com>
Sun, 10 Apr 2022 12:25:01 +0000 (20:25 +0800)
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>
.gitignore
src/Makefile
src/uring_read_fault.c [new file with mode: 0644]
tests/generic/678 [new file with mode: 0755]
tests/generic/678.out [new file with mode: 0644]

index ca6ba5c473fa067b180cf798a830b0ff7856423a..11405bd2f53dba18f4ae6aeeac939891e1fd486b 100644 (file)
@@ -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
index 4d9e02b74d13517a5e8d1d08a47db21ea22f6cf5..88877ac9de74e3ccb8be59e809910175dfffed0b 100644 (file)
@@ -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 (file)
index 0000000..9fb86c4
--- /dev/null
@@ -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 (executable)
index 0000000..1c3cf6e
--- /dev/null
@@ -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 (file)
index 0000000..452c31e
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 678
+Silence is golden