* [PATCHv2 0/2] blktests: add tests with offsets
@ 2025-11-19 19:54 Keith Busch
2025-11-19 19:54 ` [PATCHv2 1/2] blktests: test direct io offsets Keith Busch
2025-11-19 19:54 ` [PATCHv2 2/2] blktests: test io_uring user metadata offsets Keith Busch
0 siblings, 2 replies; 12+ messages in thread
From: Keith Busch @ 2025-11-19 19:54 UTC (permalink / raw)
To: linux-block, shinichiro.kawasaki; +Cc: chaitanyak, Keith Busch
From: Keith Busch <kbusch@kernel.org>
Here are the tests I've been using to check the kernel's handling for IO
using non-block aligned memory offsets for data and metadata.
Changes for the previous version:
- Combining the io_uring metadata test in one series
- Removed all the extra code that tries to discover the queue limits
from the file path. The test case will simply pass these through
from sysfs attributes that it knows about.
- Removed the linux kernel version specific checks to decide if a test
should pass or fail. We just assume a failure means the kernel
doesn't support it and carry on.
- Fixed up the test dependencies for the metadata test, and wording
- Fixed up requests from previous review comments.
Keith Busch (2):
blktests: test direct io offsets
blktests: test io_uring user metadata offsets
src/.gitignore | 2 +
src/Makefile | 17 +-
src/dio-offsets.c | 708 ++++++++++++++++++++++++++++++++++++++++++++
src/metadata.c | 488 ++++++++++++++++++++++++++++++
tests/block/042 | 26 ++
tests/block/042.out | 2 +
tests/block/043 | 33 +++
tests/block/043.out | 2 +
8 files changed, 1270 insertions(+), 8 deletions(-)
create mode 100644 src/dio-offsets.c
create mode 100644 src/metadata.c
create mode 100644 tests/block/042
create mode 100644 tests/block/042.out
create mode 100755 tests/block/043
create mode 100644 tests/block/043.out
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCHv2 1/2] blktests: test direct io offsets
2025-11-19 19:54 [PATCHv2 0/2] blktests: add tests with offsets Keith Busch
@ 2025-11-19 19:54 ` Keith Busch
2025-11-19 23:39 ` Chaitanya Kulkarni
` (2 more replies)
2025-11-19 19:54 ` [PATCHv2 2/2] blktests: test io_uring user metadata offsets Keith Busch
1 sibling, 3 replies; 12+ messages in thread
From: Keith Busch @ 2025-11-19 19:54 UTC (permalink / raw)
To: linux-block, shinichiro.kawasaki; +Cc: chaitanyak, Keith Busch
From: Keith Busch <kbusch@kernel.org>
Tests various direct IO memory and length alignments against the
device's queue limits reported from sysfs.
Signed-off-by: Keith Busch <kbusch@kernel.org>
---
src/.gitignore | 1 +
src/Makefile | 1 +
src/dio-offsets.c | 708 ++++++++++++++++++++++++++++++++++++++++++++
tests/block/042 | 26 ++
tests/block/042.out | 2 +
5 files changed, 738 insertions(+)
create mode 100644 src/dio-offsets.c
create mode 100644 tests/block/042
create mode 100644 tests/block/042.out
diff --git a/src/.gitignore b/src/.gitignore
index 2ece754..865675c 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,3 +1,4 @@
+/dio-offsets
/discontiguous-io
/loblksize
/loop_change_fd
diff --git a/src/Makefile b/src/Makefile
index ba0d9b7..179a673 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -9,6 +9,7 @@ HAVE_C_MACRO = $(shell if echo "$(H)include <$(1)>" | \
then echo 1;else echo 0; fi)
C_TARGETS := \
+ dio-offsets \
loblksize \
loop_change_fd \
loop_get_status_null \
diff --git a/src/dio-offsets.c b/src/dio-offsets.c
new file mode 100644
index 0000000..8e46091
--- /dev/null
+++ b/src/dio-offsets.c
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Meta Platforms, Inc. All Rights Reserved.
+ *
+ * Description: test direct-io memory alignment offsets
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/uio.h>
+
+#define power_of_2(x) ((x) && !((x) & ((x) - 1)))
+
+static unsigned long logical_block_size;
+static unsigned long dma_alignment;
+static unsigned long virt_boundary;
+static unsigned long max_segments;
+static unsigned long max_bytes;
+static size_t buf_size;
+static long pagesize;
+static void *out_buf;
+static void *in_buf;
+static int test_fd;
+
+static void init_args(char **argv)
+{
+ test_fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC | O_DIRECT);
+ if (test_fd < 0)
+ err(errno, "%s: failed to open %s", __func__, argv[1]);
+
+ max_segments = strtoul(argv[2], NULL, 0);
+ max_bytes = strtoul(argv[3], NULL, 0) * 1024;
+ dma_alignment = strtoul(argv[4], NULL, 0) + 1;
+ virt_boundary = strtoul(argv[5], NULL, 0) + 1;
+ logical_block_size = strtoul(argv[6], NULL, 0);
+
+ if (!power_of_2(virt_boundary) ||
+ !power_of_2(dma_alignment) ||
+ !power_of_2(logical_block_size)) {
+ errno = EINVAL;
+ err(1, "%s: bad parameters", __func__);
+ }
+
+ if (virt_boundary > 1 && virt_boundary < logical_block_size) {
+ errno = EINVAL;
+ err(1, "%s: virt_boundary:%lu logical_block_size:%lu", __func__,
+ virt_boundary, logical_block_size);
+ }
+
+ if (dma_alignment > logical_block_size) {
+ errno = EINVAL;
+ err(1, "%s: dma_alignment:%lu logical_block_size:%lu", __func__,
+ dma_alignment, logical_block_size);
+ }
+
+ if (max_segments > 4096)
+ max_segments = 4096;
+ if (max_bytes > 16384 * 1024)
+ max_bytes = 16384 * 1024;
+ if (max_bytes & (logical_block_size - 1))
+ max_bytes -= max_bytes & (logical_block_size - 1);
+
+ pagesize = sysconf(_SC_PAGE_SIZE);
+}
+
+static void init_buffers()
+{
+ unsigned long lb_mask = logical_block_size - 1;
+ int fd, ret;
+
+ buf_size = max_bytes * max_segments / 2;
+ if (buf_size < logical_block_size * max_segments)
+ err(EINVAL, "%s: logical block size is too big", __func__);
+
+ if (buf_size < logical_block_size * 1024 * 4)
+ buf_size = logical_block_size * 1024 * 4;
+
+ if (buf_size & lb_mask)
+ buf_size = (buf_size + lb_mask) & ~(lb_mask);
+
+ ret = posix_memalign((void **)&in_buf, pagesize, buf_size);
+ if (ret)
+ err(EINVAL, "%s: failed to allocate in-buf", __func__);
+
+ ret = posix_memalign((void **)&out_buf, pagesize, buf_size);
+ if (ret)
+ err(EINVAL, "%s: failed to allocate out-buf", __func__);
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ err(EINVAL, "%s: failed to open urandom", __func__);
+
+ ret = read(fd, out_buf, buf_size);
+ if (ret < 0)
+ err(EINVAL, "%s: failed to randomize output buffer", __func__);
+
+ close(fd);
+}
+
+static void __compare(void *a, void *b, size_t size, const char *test)
+{
+ if (!memcmp(a, b, size))
+ return;
+ err(EIO, "%s: data corruption", test);
+}
+#define compare(a, b, size) __compare(a, b, size, __func__)
+
+/*
+ * Test using page aligned buffers, single source
+ *
+ * Total size is aligned to a logical block size and exceeds the max transfer
+ * size as well as the max segments. This should test the kernel's split bio
+ * construction and bio splitting for exceeding these limits.
+ */
+static void test_full_size_aligned()
+{
+ int ret;
+
+ memset(in_buf, 0, buf_size);
+ ret = pwrite(test_fd, out_buf, buf_size, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to write buf", __func__);
+
+ ret = pread(test_fd, in_buf, buf_size, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ compare(out_buf, in_buf, buf_size);
+}
+
+/*
+ * Test using dma aligned buffers, single source
+ *
+ * This tests the kernel's dio memory alignment
+ */
+static void test_dma_aligned()
+{
+ int ret;
+
+ memset(in_buf, 0, buf_size);
+ ret = pwrite(test_fd, out_buf + dma_alignment, max_bytes, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to write buf", __func__);
+
+ ret = pread(test_fd, in_buf + dma_alignment, max_bytes, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ compare(out_buf + dma_alignment, in_buf + dma_alignment, max_bytes);
+}
+
+/*
+ * Test using page aligned buffers + logicaly block sized vectored source
+ *
+ * This tests discontiguous vectored sources
+ */
+static void test_page_aligned_vectors()
+{
+ const int vecs = 4;
+
+ int i, ret, offset;
+ struct iovec iov[vecs];
+
+ memset(in_buf, 0, buf_size);
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 4;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size * 2;
+ }
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to write buf", __func__);
+
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 4;
+ iov[i].iov_base = in_buf + offset;
+ iov[i].iov_len = logical_block_size * 2;
+ }
+
+ ret = preadv(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 4;
+ compare(in_buf + offset, out_buf + offset, logical_block_size * 2);
+ }
+}
+
+/*
+ * Test using dma aligned buffers, vectored source
+ *
+ * This tests discontiguous vectored sources with incrementing dma aligned
+ * offsets
+ */
+static void test_dma_aligned_vectors()
+{
+ const int vecs = 4;
+
+ int i, ret, offset;
+ struct iovec iov[vecs];
+
+ memset(in_buf, 0, buf_size);
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 8 + dma_alignment * (i + 1);
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size * 2;
+ }
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to write buf", __func__);
+
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 8 + dma_alignment * (i + 1);
+ iov[i].iov_base = in_buf + offset;
+ iov[i].iov_len = logical_block_size * 2;
+ }
+
+ ret = preadv(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 8 + dma_alignment * (i + 1);
+ compare(in_buf + offset, out_buf + offset, logical_block_size * 2);
+ }
+}
+
+/*
+ * Test vectored read with a total size aligned to a block, but some individual
+ * vectors will not be aligned to to the block size.
+ *
+ * All the middle vectors start and end on page boundaries which should
+ * satisfy any virt_boundary condition. This test will fail prior to kernel
+ * 6.18.
+ */
+static void test_unaligned_page_vectors()
+{
+ const int vecs = 4;
+
+ int i, ret, offset, mult;
+ struct iovec iov[vecs];
+ bool should_fail = true;
+
+ i = 0;
+ memset(in_buf, 0, buf_size);
+ mult = pagesize / logical_block_size;
+ if (mult < 2)
+ mult = 2;
+
+ offset = pagesize - (logical_block_size / 4);
+ if (offset & (dma_alignment - 1))
+ offset = pagesize - dma_alignment;
+
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = pagesize - offset;
+
+ for (i = 1; i < vecs - 1; i++) {
+ offset = logical_block_size * i * 8 * mult;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size * mult;
+ }
+
+ offset = logical_block_size * i * 8 * mult;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size * mult - iov[0].iov_len;
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0) {
+ if (should_fail)
+ return;
+ err(errno, "%s: failed to write buf", __func__);
+ }
+
+ i = 0;
+ offset = pagesize - (logical_block_size / 4);
+ if (offset & (dma_alignment - 1))
+ offset = pagesize - dma_alignment;
+
+ iov[i].iov_base = in_buf + offset;
+ iov[i].iov_len = pagesize - offset;
+
+ for (i = 1; i < vecs - 1; i++) {
+ offset = logical_block_size * i * 8 * mult;
+ iov[i].iov_base = in_buf + offset;
+ iov[i].iov_len = logical_block_size * mult;
+ }
+
+ offset = logical_block_size * i * 8 * mult;
+ iov[i].iov_base = in_buf + offset;
+ iov[i].iov_len = logical_block_size * mult - iov[0].iov_len;
+
+ ret = preadv(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ i = 0;
+ offset = pagesize - (logical_block_size / 4);
+ if (offset & (dma_alignment - 1))
+ offset = pagesize - dma_alignment;
+
+ compare(in_buf + offset, out_buf + offset, iov[i].iov_len);
+ for (i = 1; i < vecs - 1; i++) {
+ offset = logical_block_size * i * 8 * mult;
+ compare(in_buf + offset, out_buf + offset, iov[i].iov_len);
+ }
+ offset = logical_block_size * i * 8 * mult;
+ compare(in_buf + offset, out_buf + offset, iov[i].iov_len);
+}
+
+/*
+ * Total size is a logical block size multiple, but none of the vectors are.
+ *
+ * Total vectors will be less than the max. The vectors will be dma aligned. If
+ * a virtual boundary exists, this should fail, otherwise it should succceed on
+ * kernels 6.18 and newer.
+ */
+static void test_unaligned_vectors()
+{
+ const int vecs = 4;
+
+ struct iovec iov[vecs];
+ int i, ret, offset;
+
+ memset(in_buf, 0, buf_size);
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 8;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size / 2;
+ }
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ return;
+
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 8;
+ iov[i].iov_base = in_buf + offset;
+ iov[i].iov_len = logical_block_size / 2;
+ }
+
+ ret = preadv(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ for (i = 0; i < vecs; i++) {
+ offset = logical_block_size * i * 8;
+ compare(in_buf + offset, out_buf + offset, logical_block_size / 2);
+ }
+}
+
+/*
+ * Provide an invalid iov_base at the beginning to test the kernel catching it
+ * while building a bio.
+ */
+static void test_invalid_starting_addr()
+{
+ const int vecs = 4;
+
+ int i, ret, offset;
+ struct iovec iov[vecs];
+
+ i = 0;
+ iov[i].iov_base = 0;
+ iov[i].iov_len = logical_block_size;
+
+ for (i = 1; i < vecs; i++) {
+ offset = logical_block_size * i * 8;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size;
+ }
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ return;
+
+ err(ENOTSUP, "%s: write buf unexpectedly succeeded with NULL address ret:%d",
+ __func__, ret);
+}
+
+/*
+ * Provide an invalid iov_base in the middle to test the kernel catching it
+ * while building split bios. Ensure it is split by sending enough vectors to
+ * exceed bio's MAX_VEC; this should cause part of the io to dispatch.
+ */
+static void test_invalid_middle_addr()
+{
+ const int vecs = 1024;
+
+ int i, ret, offset;
+ struct iovec iov[vecs];
+
+ for (i = 0; i < vecs / 2 + 1; i++) {
+ offset = logical_block_size * i * 2;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size;
+ }
+
+ offset = logical_block_size * i * 2;
+ iov[i].iov_base = 0;
+ iov[i].iov_len = logical_block_size;
+
+ for (++i; i < vecs; i++) {
+ offset = logical_block_size * i * 2;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = logical_block_size;
+ }
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ return;
+
+ err(ENOTSUP, "%s: write buf unexpectedly succeeded with NULL address ret:%d",
+ __func__, ret);
+}
+
+/*
+ * Test with an invalid DMA address. Should get caught early when splitting. If
+ * the device supports byte aligned memory (which is unusual), then this should
+ * be successful.
+ */
+static void test_invalid_dma_alignment()
+{
+ int ret, offset;
+ size_t size;
+ bool should_fail = dma_alignment > 1;
+
+ memset(in_buf, 0, buf_size);
+ offset = 2 * dma_alignment - 1;
+ size = logical_block_size * 256;
+ ret = pwrite(test_fd, out_buf + offset, size, 0);
+ if (ret < 0) {
+ if (should_fail)
+ return;
+ err(errno, "%s: failed to write buf", __func__);
+ }
+
+ if (should_fail)
+ err(ENOTSUP, "%s: write buf unexpectedly succeeded with invalid DMA offset address, ret:%d",
+ __func__, ret);
+
+ ret = pread(test_fd, in_buf + offset, size, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ compare(out_buf + offset, in_buf + offset, size);
+}
+
+/*
+ * Test with invalid DMA alignment in the middle. This should get split with
+ * the first part being dispatched, and the 2nd one failing without dispatch.
+ */
+static void test_invalid_dma_vector_alignment()
+{
+ const int vecs = 5;
+
+ bool should_fail = dma_alignment > 1;
+ struct iovec iov[vecs];
+ int ret, offset;
+
+ offset = dma_alignment * 2 - 1;
+ memset(in_buf, 0, buf_size);
+
+ iov[0].iov_base = out_buf;
+ iov[0].iov_len = max_bytes;
+
+ iov[1].iov_base = out_buf + max_bytes * 2;
+ iov[1].iov_len = max_bytes;
+
+ iov[2].iov_base = out_buf + max_bytes * 4 + offset;
+ iov[2].iov_len = max_bytes;
+
+ iov[3].iov_base = out_buf + max_bytes * 6;
+ iov[3].iov_len = max_bytes;
+
+ iov[4].iov_base = out_buf + max_bytes * 8;
+ iov[4].iov_len = max_bytes;
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0) {
+ if (should_fail)
+ return;
+ err(errno, "%s: failed to write buf", __func__);
+ }
+ if (should_fail)
+ err(ENOTSUP, "%s: write buf unexpectedly succeeded with invalid DMA offset address ret:%d",
+ __func__, ret);
+
+ iov[0].iov_base = in_buf;
+ iov[0].iov_len = max_bytes;
+
+ iov[1].iov_base = in_buf + max_bytes * 2;
+ iov[1].iov_len = max_bytes;
+
+ iov[2].iov_base = in_buf + max_bytes * 4 + offset;
+ iov[2].iov_len = max_bytes;
+
+ iov[3].iov_base = in_buf + max_bytes * 6;
+ iov[3].iov_len = max_bytes;
+
+ iov[4].iov_base = in_buf + max_bytes * 8;
+ iov[4].iov_len = max_bytes;
+
+ ret = preadv(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ compare(out_buf, in_buf, max_bytes);
+ compare(out_buf + max_bytes * 2, in_buf + max_bytes * 2, max_bytes);
+ compare(out_buf + max_bytes * 4 + offset, in_buf + max_bytes * 4 + offset, max_bytes);
+ compare(out_buf + max_bytes * 6, in_buf + max_bytes * 6, max_bytes);
+ compare(out_buf + max_bytes * 8, in_buf + max_bytes * 8, max_bytes);
+}
+
+/*
+ * Test a bunch of small vectors if the device dma alignemnt allows it. We'll
+ * try to force a MAX_IOV split that can't form a valid IO so expect a failure.
+ */
+static void test_max_vector_limits()
+{
+ const int vecs = 320;
+
+ int ret, i, offset, iovpb, iov_size;
+ bool should_fail = true;
+ struct iovec iov[vecs];
+
+ memset(in_buf, 0, buf_size);
+ iovpb = logical_block_size / dma_alignment;
+ iov_size = logical_block_size / iovpb;
+
+ if ((pagesize / iov_size) < 256 &&
+ iov_size >= virt_boundary)
+ should_fail = false;
+
+ for (i = 0; i < vecs; i++) {
+ offset = i * iov_size * 2;
+ iov[i].iov_base = out_buf + offset;
+ iov[i].iov_len = iov_size;
+ }
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0) {
+ if (should_fail)
+ return;
+ err(errno, "%s: failed to write buf", __func__);
+ }
+
+ if (should_fail)
+ err(ENOTSUP, "%s: write buf unexpectedly succeeded with excess vectors ret:%d",
+ __func__, ret);
+
+ for (i = 0; i < vecs; i++) {
+ offset = i * iov_size * 2;
+ iov[i].iov_base = in_buf + offset;
+ iov[i].iov_len = iov_size;
+ }
+
+ ret = preadv(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ for (i = 0; i < vecs; i++) {
+ offset = i * iov_size * 2;
+ compare(in_buf + offset, out_buf + offset, logical_block_size / 2);
+ }
+}
+
+/*
+ * Start with a valid vector that can be split into a dispatched IO, but poison
+ * the rest with an invalid DMA offset testing the kernel's late catch.
+ */
+static void test_invalid_dma_vector_alignment_large()
+{
+ const int vecs = 4;
+
+ struct iovec iov[vecs];
+ int i, ret;
+
+ i = 0;
+ iov[i].iov_base = out_buf;
+ iov[i].iov_len = max_bytes - logical_block_size;
+
+ i++;
+ iov[i].iov_base = out_buf + max_bytes + logical_block_size;
+ iov[i].iov_len = logical_block_size;
+
+ i++;
+ iov[i].iov_base = iov[1].iov_base + pagesize * 2 + (dma_alignment - 1);
+ iov[i].iov_len = logical_block_size;
+
+ i++;
+ iov[i].iov_base = out_buf + max_bytes * 8;
+ iov[i].iov_len = logical_block_size;
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ return;
+
+ err(ENOTSUP, "%s: write buf unexpectedly succeeded with NULL address ret:%d",
+ __func__, ret);
+}
+
+/*
+ * Total size is block aligned, addresses are dma aligned, but invidual vector
+ * sizes may not be dma aligned. If device has byte sized dma alignment, this
+ * should succeed. If not, part of this should get dispatched, and the other
+ * part should fail.
+ */
+static void test_invalid_dma_vector_length()
+{
+ const int vecs = 4;
+
+ bool should_fail = dma_alignment > 1;
+ struct iovec iov[vecs];
+ int ret;
+
+ iov[0].iov_base = out_buf;
+ iov[0].iov_len = max_bytes * 2 - max_bytes / 2;
+
+ iov[1].iov_base = out_buf + max_bytes * 4;
+ iov[1].iov_len = logical_block_size * 2 - (dma_alignment + 1);
+
+ iov[2].iov_base = out_buf + max_bytes * 8;
+ iov[2].iov_len = logical_block_size * 2 + (dma_alignment + 1);
+
+ iov[3].iov_base = out_buf + max_bytes * 12;
+ iov[3].iov_len = max_bytes - max_bytes / 2;
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0) {
+ if (should_fail)
+ return;
+ err(errno, "%s: failed to write buf", __func__);
+ }
+
+ if (should_fail)
+ err(ENOTSUP, "%s: write buf unexpectedly succeeded with invalid DMA offset address ret:%d",
+ __func__, ret);
+
+ iov[0].iov_base = in_buf;
+ iov[0].iov_len = max_bytes * 2 - max_bytes / 2;
+
+ iov[1].iov_base = in_buf + max_bytes * 4;
+ iov[1].iov_len = logical_block_size * 2 - (dma_alignment + 1);
+
+ iov[2].iov_base = in_buf + max_bytes * 8;
+ iov[2].iov_len = logical_block_size * 2 + (dma_alignment + 1);
+
+ iov[3].iov_base = in_buf + max_bytes * 12;
+ iov[3].iov_len = max_bytes - max_bytes / 2;
+
+ ret = pwritev(test_fd, iov, vecs, 0);
+ if (ret < 0)
+ err(errno, "%s: failed to read buf", __func__);
+
+ compare(out_buf, in_buf, iov[0].iov_len);
+ compare(out_buf + max_bytes * 4, in_buf + max_bytes * 4, iov[1].iov_len);
+ compare(out_buf + max_bytes * 8, in_buf + max_bytes * 8, iov[2].iov_len);
+ compare(out_buf + max_bytes * 12, in_buf + max_bytes * 12, iov[3].iov_len);
+}
+
+static void run_tests()
+{
+ test_full_size_aligned();
+ test_dma_aligned();
+ test_page_aligned_vectors();
+ test_dma_aligned_vectors();
+ test_unaligned_page_vectors();
+ test_unaligned_vectors();
+ test_invalid_starting_addr();
+ test_invalid_middle_addr();
+ test_invalid_dma_alignment();
+ test_invalid_dma_vector_alignment();
+ test_max_vector_limits();
+ test_invalid_dma_vector_alignment_large();
+ test_invalid_dma_vector_length();
+}
+
+/* ./$prog-name file */
+int main(int argc, char **argv)
+{
+ if (argc < 2)
+ errx(EINVAL, "expect argments: file");
+
+ init_args(argv);
+ init_buffers();
+ run_tests();
+ close(test_fd);
+ free(out_buf);
+ free(in_buf);
+
+ return 0;
diff --git a/tests/block/042 b/tests/block/042
new file mode 100644
index 0000000..a911d82
--- /dev/null
+++ b/tests/block/042
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+. tests/block/rc
+
+DESCRIPTION="Test unusual direct-io offsets"
+QUICK=1
+
+device_requires() {
+ _require_test_dev_sysfs
+}
+
+test_device() {
+ echo "Running ${TEST_NAME}"
+
+ sys_max_segments=$(cat "${TEST_DEV_SYSFS}/queue/max_segments")
+ sys_dma_alignment=$(cat "${TEST_DEV_SYSFS}/queue/dma_alignment")
+ sys_virt_boundary_mask=$(cat "${TEST_DEV_SYSFS}/queue/virt_boundary_mask")
+ sys_logical_block_size=$(cat "${TEST_DEV_SYSFS}/queue/logical_block_size")
+ sys_max_sectors_kb=$(cat "${TEST_DEV_SYSFS}/queue/max_sectors_kb")
+
+ if ! src/dio-offsets ${TEST_DEV} $sys_max_segments $sys_max_sectors_kb $sys_dma_alignment $sys_virt_boundary_mask $sys_logical_block_size; then
+ echo "src/dio-offsets failed"
+ fi
+
+ echo "Test complete"
+}
diff --git a/tests/block/042.out b/tests/block/042.out
new file mode 100644
index 0000000..b35c7ce
--- /dev/null
+++ b/tests/block/042.out
@@ -0,0 +1,2 @@
+Running block/042
+Test complete
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCHv2 2/2] blktests: test io_uring user metadata offsets
2025-11-19 19:54 [PATCHv2 0/2] blktests: add tests with offsets Keith Busch
2025-11-19 19:54 ` [PATCHv2 1/2] blktests: test direct io offsets Keith Busch
@ 2025-11-19 19:54 ` Keith Busch
2025-11-19 23:39 ` Chaitanya Kulkarni
1 sibling, 1 reply; 12+ messages in thread
From: Keith Busch @ 2025-11-19 19:54 UTC (permalink / raw)
To: linux-block, shinichiro.kawasaki; +Cc: chaitanyak, Keith Busch
From: Keith Busch <kbusch@kernel.org>
For devices with metadata, tests various userspace offsets with
io_uring capabilities. If the metadata is formatted with ref tag
protection information, test various seed offsets as well.
Signed-off-by: Keith Busch <kbusch@kernel.org>
---
src/.gitignore | 1 +
src/Makefile | 16 +-
src/metadata.c | 488 ++++++++++++++++++++++++++++++++++++++++++++
tests/block/043 | 33 +++
tests/block/043.out | 2 +
5 files changed, 532 insertions(+), 8 deletions(-)
create mode 100644 src/metadata.c
create mode 100755 tests/block/043
create mode 100644 tests/block/043.out
diff --git a/src/.gitignore b/src/.gitignore
index 865675c..e6c6c38 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -3,6 +3,7 @@
/loblksize
/loop_change_fd
/loop_get_status_null
+/metadata
/mount_clear_sock
/nbdsetsize
/openclose
diff --git a/src/Makefile b/src/Makefile
index 179a673..dac07c7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -22,7 +22,8 @@ C_TARGETS := \
sg/syzkaller1 \
zbdioctl
-C_MINIUBLK := miniublk
+C_URING_TARGETS := miniublk \
+ metadata
HAVE_LIBURING := $(call HAVE_C_MACRO,liburing.h,IORING_OP_URING_CMD)
HAVE_UBLK_HEADER := $(call HAVE_C_HEADER,linux/ublk_cmd.h,1)
@@ -31,9 +32,9 @@ CXX_TARGETS := \
discontiguous-io
ifeq ($(HAVE_LIBURING)$(HAVE_UBLK_HEADER), 11)
-TARGETS := $(C_TARGETS) $(CXX_TARGETS) $(C_MINIUBLK)
+TARGETS := $(C_TARGETS) $(CXX_TARGETS) $(C_URING_TARGETS)
else
-$(info Skip $(C_MINIUBLK) build due to missing kernel header(v6.0+) or liburing(2.2+))
+$(info Skip $(C_URING_TARGETS) build due to missing kernel header(v6.0+) or liburing(2.2+))
TARGETS := $(C_TARGETS) $(CXX_TARGETS)
endif
@@ -42,8 +43,8 @@ CONFIG_DEFS := $(call HAVE_C_HEADER,linux/blkzoned.h,-DHAVE_LINUX_BLKZONED_H)
override CFLAGS := -O2 -Wall -Wshadow $(CFLAGS) $(CONFIG_DEFS)
override CXXFLAGS := -O2 -std=c++11 -Wall -Wextra -Wshadow -Wno-sign-compare \
-Werror $(CXXFLAGS) $(CONFIG_DEFS)
-MINIUBLK_FLAGS := -D_GNU_SOURCE
-MINIUBLK_LIBS := -lpthread -luring
+URING_FLAGS := -D_GNU_SOURCE
+URING_LIBS := -lpthread -luring
LDFLAGS ?=
all: $(TARGETS)
@@ -61,8 +62,7 @@ $(C_TARGETS): %: %.c
$(CXX_TARGETS): %: %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $^
-$(C_MINIUBLK): %: miniublk.c
- $(CC) $(CFLAGS) $(LDFLAGS) $(MINIUBLK_FLAGS) -o $@ miniublk.c \
- $(MINIUBLK_LIBS)
+$(C_URING_TARGETS): %: %.c
+ $(CC) $(CFLAGS) $(LDFLAGS) $(URING_FLAGS) -o $@ $^ $(URING_LIBS)
.PHONY: all clean install
diff --git a/src/metadata.c b/src/metadata.c
new file mode 100644
index 0000000..d935fd6
--- /dev/null
+++ b/src/metadata.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-3.0+
+/*
+ * Copyright (c) 2025 Meta Platforms, Inc. All Rights Reserved.
+ *
+ * Description: test userspace metadata
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <liburing.h>
+
+#ifndef IORING_RW_ATTR_FLAG_PI
+#define PI_URING_COMPAT
+#define IORING_RW_ATTR_FLAG_PI (1U << 0)
+/* PI attribute information */
+struct io_uring_attr_pi {
+ __u16 flags;
+ __u16 app_tag;
+ __u32 len;
+ __u64 addr;
+ __u64 seed;
+ __u64 rsvd;
+};
+#endif
+
+#ifndef FS_IOC_GETLBMD_CAP
+/* Protection info capability flags */
+#define LBMD_PI_CAP_INTEGRITY (1 << 0)
+#define LBMD_PI_CAP_REFTAG (1 << 1)
+
+/* Checksum types for Protection Information */
+#define LBMD_PI_CSUM_NONE 0
+#define LBMD_PI_CSUM_IP 1
+#define LBMD_PI_CSUM_CRC16_T10DIF 2
+#define LBMD_PI_CSUM_CRC64_NVME 4
+
+/*
+ * Logical block metadata capability descriptor
+ * If the device does not support metadata, all the fields will be zero.
+ * Applications must check lbmd_flags to determine whether metadata is
+ * supported or not.
+ */
+struct logical_block_metadata_cap {
+ /* Bitmask of logical block metadata capability flags */
+ __u32 lbmd_flags;
+ /*
+ * The amount of data described by each unit of logical block
+ * metadata
+ */
+ __u16 lbmd_interval;
+ /*
+ * Size in bytes of the logical block metadata associated with each
+ * interval
+ */
+ __u8 lbmd_size;
+ /*
+ * Size in bytes of the opaque block tag associated with each
+ * interval
+ */
+ __u8 lbmd_opaque_size;
+ /*
+ * Offset in bytes of the opaque block tag within the logical block
+ * metadata
+ */
+ __u8 lbmd_opaque_offset;
+ /* Size in bytes of the T10 PI tuple associated with each interval */
+ __u8 lbmd_pi_size;
+ /* Offset in bytes of T10 PI tuple within the logical block metadata */
+ __u8 lbmd_pi_offset;
+ /* T10 PI guard tag type */
+ __u8 lbmd_guard_tag_type;
+ /* Size in bytes of the T10 PI application tag */
+ __u8 lbmd_app_tag_size;
+ /* Size in bytes of the T10 PI reference tag */
+ __u8 lbmd_ref_tag_size;
+ /* Size in bytes of the T10 PI storage tag */
+ __u8 lbmd_storage_tag_size;
+ __u8 pad;
+};
+
+#define FS_IOC_GETLBMD_CAP _IOWR(0x15, 2, struct logical_block_metadata_cap)
+#endif /* FS_IOC_GETLBMD_CAP */
+
+#ifndef IO_INTEGRITY_CHK_GUARD
+/* flags for integrity meta */
+#define IO_INTEGRITY_CHK_GUARD (1U << 0) /* enforce guard check */
+#define IO_INTEGRITY_CHK_REFTAG (1U << 1) /* enforce ref check */
+#define IO_INTEGRITY_CHK_APPTAG (1U << 2) /* enforce app check */
+#endif /* IO_INTEGRITY_CHK_GUARD */
+
+/* This size should guarantee at least one split */
+#define DATA_SIZE (8 * 1024 * 1024)
+
+static unsigned short lba_size;
+static unsigned char metadata_size;
+static unsigned char pi_size;
+static unsigned char pi_offset;
+static bool reftag_enabled;
+
+static long pagesize;
+
+struct t10_pi_tuple {
+ __be16 guard_tag; /* Checksum */
+ __be16 app_tag; /* Opaque storage */
+ __be32 ref_tag; /* Target LBA or indirect LBA */
+};
+
+struct crc64_pi_tuple {
+ __be64 guard_tag;
+ __be16 app_tag;
+ __u8 ref_tag[6];
+};
+
+static int init_capabilities(int fd)
+{
+ struct logical_block_metadata_cap md_cap;
+ int ret;
+
+ ret = ioctl(fd, FS_IOC_GETLBMD_CAP, &md_cap);
+ if (ret < 0) {
+ perror("FS_IOC_GETLBMD_CAP");
+ return ret;
+ }
+
+ lba_size = md_cap.lbmd_interval;
+ metadata_size = md_cap.lbmd_size;
+ pi_size = md_cap.lbmd_pi_size;
+ pi_offset = md_cap.lbmd_pi_offset;
+ reftag_enabled = md_cap.lbmd_flags & LBMD_PI_CAP_REFTAG;
+
+ pagesize = sysconf(_SC_PAGE_SIZE);
+ return 0;
+}
+
+static unsigned int swap(unsigned int value)
+{
+ return ((value >> 24) & 0x000000ff) |
+ ((value >> 8) & 0x0000ff00) |
+ ((value << 8) & 0x00ff0000) |
+ ((value << 24) & 0xff000000);
+}
+
+static inline void __put_unaligned_be48(const __u64 val, __u8 *p)
+{
+ *p++ = (val >> 40) & 0xff;
+ *p++ = (val >> 32) & 0xff;
+ *p++ = (val >> 24) & 0xff;
+ *p++ = (val >> 16) & 0xff;
+ *p++ = (val >> 8) & 0xff;
+ *p++ = val & 0xff;
+}
+
+static inline void put_unaligned_be48(const __u64 val, void *p)
+{
+ __put_unaligned_be48(val, p);
+}
+
+static inline __u64 __get_unaligned_be48(const __u8 *p)
+{
+ return (__u64)p[0] << 40 | (__u64)p[1] << 32 | (__u64)p[2] << 24 |
+ p[3] << 16 | p[4] << 8 | p[5];
+}
+
+static inline __u64 get_unaligned_be48(const void *p)
+{
+ return __get_unaligned_be48(p);
+}
+
+static void init_metadata(void *p, int intervals, int ref)
+{
+ int i, j;
+
+ for (i = 0; i < intervals; i++, ref++) {
+ int remaining = metadata_size - pi_offset;
+ unsigned char *m = p;
+
+ for (j = 0; j < pi_offset; j++)
+ m[j] = (unsigned char)(ref + j + i);
+
+ p += pi_offset;
+ if (reftag_enabled) {
+ if (pi_size == 8) {
+ struct t10_pi_tuple *tuple = p;
+
+ tuple->ref_tag = swap(ref);
+ remaining -= sizeof(*tuple);
+ p += sizeof(*tuple);
+ } else if (pi_size == 16) {
+ struct crc64_pi_tuple *tuple = p;
+
+ __put_unaligned_be48(ref, tuple->ref_tag);
+ remaining -= sizeof(*tuple);
+ p += sizeof(*tuple);
+ }
+ }
+
+ m = p;
+ for (j = 0; j < remaining; j++)
+ m[j] = (unsigned char)~(ref + j + i);
+
+ p += remaining;
+ }
+}
+
+static int check_metadata(void *p, int intervals, int ref)
+{
+ int i, j;
+
+ for (i = 0; i < intervals; i++, ref++) {
+ int remaining = metadata_size - pi_offset;
+ unsigned char *m = p;
+
+ for (j = 0; j < pi_offset; j++) {
+ if (m[j] != (unsigned char)(ref + j + i)) {
+ fprintf(stderr, "(pre)interval:%d byte:%d expected:%x got:%x\n",
+ i, j, (unsigned char)(ref + j + i), m[j]);
+ return -1;
+ }
+ }
+
+ p += pi_offset;
+ if (reftag_enabled) {
+ if (pi_size == 8) {
+ struct t10_pi_tuple *tuple = p;
+
+ if (swap(tuple->ref_tag) != ref) {
+ fprintf(stderr, "reftag interval:%d expected:%x got:%x\n",
+ i, ref, swap(tuple->ref_tag));
+ return -1;
+ }
+
+ remaining -= sizeof(*tuple);
+ p += sizeof(*tuple);
+ } else if (pi_size == 16) {
+ struct crc64_pi_tuple *tuple = p;
+ __u64 v = get_unaligned_be48(tuple->ref_tag);
+
+ if (v != ref) {
+ fprintf(stderr, "reftag interval:%d expected:%x got:%llx\n",
+ i, ref, v);
+ return -1;
+ }
+ remaining -= sizeof(*tuple);
+ p += sizeof(*tuple);
+ }
+ }
+
+ m = p;
+ for (j = 0; j < remaining; j++) {
+ if (m[j] != (unsigned char)~(ref + j + i)) {
+ fprintf(stderr, "(post)interval:%d byte:%d expected:%x got:%x\n",
+ i, j, (unsigned char)~(ref + j + i), m[j]);
+ return -1;
+ }
+ }
+
+ p += remaining;
+ }
+
+ return 0;
+}
+
+static void init_data(void *data, int offset)
+{
+ unsigned char *d = data;
+ int i;
+
+ for (i = 0; i < DATA_SIZE; i++)
+ d[i] = (unsigned char)(0xaa + offset + i);
+}
+
+static int check_data(void *data, int offset)
+{
+ unsigned char *d = data;
+ int i;
+
+ for (i = 0; i < DATA_SIZE; i++)
+ if (d[i] != (unsigned char)(0xaa + offset + i))
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, ret, i, offset, intervals, metabuffer_size, metabuffer_tx_size;
+ void *orig_data_buf, *orig_pi_buf, *data_buf;
+ struct io_uring_cqe *cqes[2];
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <dev>\n", argv[0]);
+ return 1;
+ }
+
+ fd = open(argv[1], O_RDWR | O_DIRECT);
+ if (fd < 0) {
+ perror("Failed to open device with O_DIRECT");
+ return 1;
+ }
+
+ ret = init_capabilities(fd);
+ if (ret < 0)
+ return 1;
+ if (lba_size == 0 || metadata_size == 0)
+ return 1;
+
+ intervals = DATA_SIZE / lba_size;
+ metabuffer_tx_size = intervals * metadata_size;
+ metabuffer_size = metabuffer_tx_size * 2;
+
+ if (posix_memalign(&orig_data_buf, pagesize, DATA_SIZE)) {
+ perror("posix_memalign failed for data buffer");
+ ret = 1;
+ goto close;
+ }
+
+ if (posix_memalign(&orig_pi_buf, pagesize, metabuffer_size)) {
+ perror("posix_memalign failed for metadata buffer");
+ ret = 1;
+ goto free;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ perror("io_uring_queue_init failed");
+ goto cleanup;
+ }
+
+ data_buf = orig_data_buf;
+ for (offset = 0; offset < 512; offset++) {
+ void *pi_buf = (char *)orig_pi_buf + offset * 4;
+ struct io_uring_attr_pi pi_attr = {
+ .addr = (__u64)pi_buf,
+ .seed = offset,
+ .len = metabuffer_tx_size,
+ };
+
+ if (reftag_enabled)
+ pi_attr.flags = IO_INTEGRITY_CHK_REFTAG;
+
+ init_data(data_buf, offset);
+ init_metadata(pi_buf, intervals, offset);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "Failed to get SQE\n");
+ ret = 1;
+ goto ring_exit;
+ }
+
+ io_uring_prep_write(sqe, fd, data_buf, DATA_SIZE, offset * lba_size * 8);
+ io_uring_sqe_set_data(sqe, (void *)1L);
+
+#ifdef PI_URING_COMPAT
+ /* old liburing, use fields that overlap in the union */
+ sqe->__pad2[0] = IORING_RW_ATTR_FLAG_PI;
+ sqe->addr3 = (__u64)&pi_attr;
+#else
+ sqe->attr_type_mask = IORING_RW_ATTR_FLAG_PI;
+ sqe->attr_ptr = (__u64)&pi_attr;
+#endif
+ ret = io_uring_submit(&ring);
+ if (ret < 1) {
+ perror("io_uring_submit failed (WRITE)");
+ ret = 1;
+ goto ring_exit;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ perror("io_uring_wait_cqe failed (WRITE)");
+ ret = 1;
+ goto ring_exit;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "write failed at offset %d: %s\n",
+ offset, strerror(-cqe->res));
+ ret = 1;
+ goto ring_exit;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+
+ memset(data_buf, 0, DATA_SIZE);
+ memset(pi_buf, 0, metabuffer_tx_size);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get SQE\n");
+ ret = 1;
+ goto ring_exit;
+ }
+
+ io_uring_prep_read(sqe, fd, data_buf, DATA_SIZE, offset * lba_size * 8);
+ io_uring_sqe_set_data(sqe, (void *)2L);
+
+#ifdef PI_URING_COMPAT
+ sqe->__pad2[0] = IORING_RW_ATTR_FLAG_PI;
+ sqe->addr3 = (__u64)&pi_attr;
+#else
+ sqe->attr_type_mask = IORING_RW_ATTR_FLAG_PI;
+ sqe->attr_ptr = (__u64)&pi_attr;
+#endif
+
+ ret = io_uring_submit(&ring);
+ if (ret < 1) {
+ perror("io_uring_submit failed (read)");
+ ret = 1;
+ goto ring_exit;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_wait_cqe failed (read): %s\n", strerror(-ret));
+ ret = 1;
+ goto ring_exit;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "read failed at offset %d: %s\n",
+ offset, strerror(-cqe->res));
+ ret = 1;
+ goto ring_exit;
+ }
+
+ ret = check_data(data_buf, offset);
+ if (ret) {
+ fprintf(stderr, "data corruption at offset %d\n",
+ offset);
+ ret = 1;
+ goto ring_exit;
+ }
+
+ ret = check_metadata(pi_buf, intervals, offset);
+ if (ret) {
+ fprintf(stderr, "metadata corruption at offset %d\n",
+ offset);
+ ret = 1;
+ goto ring_exit;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ memset(data_buf, 0, DATA_SIZE);
+ for (i = 0; i < 2; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "failed get sqe\n");
+ ret = 1;
+ goto ring_exit;
+ }
+
+ io_uring_prep_write(sqe, fd, data_buf, DATA_SIZE, DATA_SIZE * i);
+ io_uring_sqe_set_data(sqe, (void *)(uintptr_t)i + 1);
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret < 1) {
+ fprintf(stderr, "failed to submit sqes\n");
+ goto ring_exit;
+ }
+ ret = io_uring_wait_cqe_nr(&ring, cqes, 2);
+ if (ret)
+ fprintf(stderr, "failed to reap cqes\n");
+ring_exit:
+ io_uring_queue_exit(&ring);
+cleanup:
+ free(orig_pi_buf);
+free:
+ free(orig_data_buf);
+close:
+ close(fd);
+ return ret;
+}
diff --git a/tests/block/043 b/tests/block/043
new file mode 100755
index 0000000..dcd80d4
--- /dev/null
+++ b/tests/block/043
@@ -0,0 +1,33 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2025 Keith Busch <kbusch@kernel.org>
+#
+# Tests various user space metadata offsets with io_uring capabilities. If the
+# format uses ref tag protection, test various seed offsets as well.
+
+. tests/block/rc
+. common/nvme
+
+DESCRIPTION="Test userspace metadata offsets"
+QUICK=1
+
+device_requires() {
+ _test_dev_has_metadata
+ _test_dev_disables_extended_lba
+}
+
+requires() {
+ _have_kernel_option IO_URING
+ _have_kernel_option BLK_DEV_INTEGRITY
+}
+
+test_device() {
+ echo "Running ${TEST_NAME}"
+
+ if ! src/metadata ${TEST_DEV}; then
+ echo "src/metadata failed"
+ fi
+
+ echo "Test complete"
+}
+
diff --git a/tests/block/043.out b/tests/block/043.out
new file mode 100644
index 0000000..fda7fca
--- /dev/null
+++ b/tests/block/043.out
@@ -0,0 +1,2 @@
+Running block/043
+Test complete
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-19 19:54 ` [PATCHv2 1/2] blktests: test direct io offsets Keith Busch
@ 2025-11-19 23:39 ` Chaitanya Kulkarni
2025-11-19 23:48 ` Chaitanya Kulkarni
2025-11-25 11:26 ` Shinichiro Kawasaki
2 siblings, 0 replies; 12+ messages in thread
From: Chaitanya Kulkarni @ 2025-11-19 23:39 UTC (permalink / raw)
To: Keith Busch, linux-block@vger.kernel.org,
shinichiro.kawasaki@wdc.com
Cc: Keith Busch
On 11/19/25 11:54, Keith Busch wrote:
> From: Keith Busch <kbusch@kernel.org>
>
> Tests various direct IO memory and length alignments against the
> device's queue limits reported from sysfs.
>
> Signed-off-by: Keith Busch <kbusch@kernel.org>
> ---
[ ... ]
> +static void test_page_aligned_vectors()
> +{
> + const int vecs = 4;
> +
> + int i, ret, offset;
> + struct iovec iov[vecs];
> +
every const int vecs has unnecessary new line like above all over
this code.
otherwise, Looks good.
Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
-ck
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv2 2/2] blktests: test io_uring user metadata offsets
2025-11-19 19:54 ` [PATCHv2 2/2] blktests: test io_uring user metadata offsets Keith Busch
@ 2025-11-19 23:39 ` Chaitanya Kulkarni
0 siblings, 0 replies; 12+ messages in thread
From: Chaitanya Kulkarni @ 2025-11-19 23:39 UTC (permalink / raw)
To: Keith Busch, linux-block@vger.kernel.org,
shinichiro.kawasaki@wdc.com
Cc: Keith Busch
On 11/19/25 11:54, Keith Busch wrote:
> From: Keith Busch<kbusch@kernel.org>
>
> For devices with metadata, tests various userspace offsets with
> io_uring capabilities. If the metadata is formatted with ref tag
> protection information, test various seed offsets as well.
>
> Signed-off-by: Keith Busch<kbusch@kernel.org>
Looks good.
Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
-ck
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-19 19:54 ` [PATCHv2 1/2] blktests: test direct io offsets Keith Busch
2025-11-19 23:39 ` Chaitanya Kulkarni
@ 2025-11-19 23:48 ` Chaitanya Kulkarni
2025-11-19 23:59 ` Keith Busch
2025-11-25 11:26 ` Shinichiro Kawasaki
2 siblings, 1 reply; 12+ messages in thread
From: Chaitanya Kulkarni @ 2025-11-19 23:48 UTC (permalink / raw)
To: Keith Busch, linux-block@vger.kernel.org,
shinichiro.kawasaki@wdc.com
Cc: Keith Busch
On 11/19/25 11:54, Keith Busch wrote:
> +static void test_invalid_dma_vector_length()
> +{
> + const int vecs = 4;
> +
> + bool should_fail = dma_alignment > 1;
> + struct iovec iov[vecs];
> + int ret;
> +
> + iov[0].iov_base = out_buf;
> + iov[0].iov_len = max_bytes * 2 - max_bytes / 2;
> +
> + iov[1].iov_base = out_buf + max_bytes * 4;
> + iov[1].iov_len = logical_block_size * 2 - (dma_alignment + 1);
> +
> + iov[2].iov_base = out_buf + max_bytes * 8;
> + iov[2].iov_len = logical_block_size * 2 + (dma_alignment + 1);
> +
> + iov[3].iov_base = out_buf + max_bytes * 12;
> + iov[3].iov_len = max_bytes - max_bytes / 2;
> +
> + ret = pwritev(test_fd, iov, vecs, 0);
> + if (ret < 0) {
> + if (should_fail)
> + return;
> + err(errno, "%s: failed to write buf", __func__);
> + }
> +
> + if (should_fail)
> + err(ENOTSUP, "%s: write buf unexpectedly succeeded with invalid DMA offset addressret:%d",
> + __func__, ret);
> +
> + iov[0].iov_base = in_buf;
> + iov[0].iov_len = max_bytes * 2 - max_bytes / 2;
> +
> + iov[1].iov_base = in_buf + max_bytes * 4;
> + iov[1].iov_len = logical_block_size * 2 - (dma_alignment + 1);
> +
> + iov[2].iov_base = in_buf + max_bytes * 8;
> + iov[2].iov_len = logical_block_size * 2 + (dma_alignment + 1);
> +
> + iov[3].iov_base = in_buf + max_bytes * 12;
> + iov[3].iov_len = max_bytes - max_bytes / 2;
> +
> + ret = pwritev(test_fd, iov, vecs, 0);
> + if (ret < 0)
> + err(errno, "%s: failed to read buf", __func__);
> +
is pwritev correct above or it should be preadv () ?
> + compare(out_buf, in_buf, iov[0].iov_len);
> + compare(out_buf + max_bytes * 4, in_buf + max_bytes * 4, iov[1].iov_len);
> + compare(out_buf + max_bytes * 8, in_buf + max_bytes * 8, iov[2].iov_len);
> + compare(out_buf + max_bytes * 12, in_buf + max_bytes * 12, iov[3].iov_len);
> +}
> +
-ck
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-19 23:48 ` Chaitanya Kulkarni
@ 2025-11-19 23:59 ` Keith Busch
2025-11-20 0:56 ` Chaitanya Kulkarni
0 siblings, 1 reply; 12+ messages in thread
From: Keith Busch @ 2025-11-19 23:59 UTC (permalink / raw)
To: Chaitanya Kulkarni
Cc: Keith Busch, linux-block@vger.kernel.org,
shinichiro.kawasaki@wdc.com
On Wed, Nov 19, 2025 at 11:48:47PM +0000, Chaitanya Kulkarni wrote:
> On 11/19/25 11:54, Keith Busch wrote:
> > + ret = pwritev(test_fd, iov, vecs, 0);
> > + if (ret < 0)
> > + err(errno, "%s: failed to read buf", __func__);
> > +
>
>
> is pwritev correct above or it should be preadv () ?
Good eye, it should have been preadv. This part is currently unreachable
though, as it requires byte-aligned dma limits and the kernel doesn't
report such a value. But if this were to run, the test would have
falsely declared data corruption.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-19 23:59 ` Keith Busch
@ 2025-11-20 0:56 ` Chaitanya Kulkarni
2025-11-20 14:32 ` Keith Busch
0 siblings, 1 reply; 12+ messages in thread
From: Chaitanya Kulkarni @ 2025-11-20 0:56 UTC (permalink / raw)
To: Keith Busch
Cc: Keith Busch, linux-block@vger.kernel.org,
shinichiro.kawasaki@wdc.com
On 11/19/25 15:59, Keith Busch wrote:
> On Wed, Nov 19, 2025 at 11:48:47PM +0000, Chaitanya Kulkarni wrote:
>> On 11/19/25 11:54, Keith Busch wrote:
>>> + ret = pwritev(test_fd, iov, vecs, 0);
>>> + if (ret < 0)
>>> + err(errno, "%s: failed to read buf", __func__);
>>> +
>>
>> is pwritev correct above or it should be preadv () ?
> Good eye, it should have been preadv. This part is currently unreachable
> though, as it requires byte-aligned dma limits and the kernel doesn't
> report such a value. But if this were to run, the test would have
> falsely declared data corruption.
how about ?
1. Keep the code but disable it until kernel gets that support ? OR
2. add an ability to autodetect if kernel has a support before running the
test else skip test_invalid_dma_vector_length() ?
-ck
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-20 0:56 ` Chaitanya Kulkarni
@ 2025-11-20 14:32 ` Keith Busch
0 siblings, 0 replies; 12+ messages in thread
From: Keith Busch @ 2025-11-20 14:32 UTC (permalink / raw)
To: Chaitanya Kulkarni
Cc: Keith Busch, linux-block@vger.kernel.org,
shinichiro.kawasaki@wdc.com
On Thu, Nov 20, 2025 at 12:56:36AM +0000, Chaitanya Kulkarni wrote:
> On 11/19/25 15:59, Keith Busch wrote:
> > On Wed, Nov 19, 2025 at 11:48:47PM +0000, Chaitanya Kulkarni wrote:
> >> On 11/19/25 11:54, Keith Busch wrote:
> >>> + ret = pwritev(test_fd, iov, vecs, 0);
> >>> + if (ret < 0)
> >>> + err(errno, "%s: failed to read buf", __func__);
> >>> +
> >>
> >> is pwritev correct above or it should be preadv () ?
> > Good eye, it should have been preadv. This part is currently unreachable
> > though, as it requires byte-aligned dma limits and the kernel doesn't
> > report such a value. But if this were to run, the test would have
> > falsely declared data corruption.
>
> how about ?
>
> 1. Keep the code but disable it until kernel gets that support ? OR
> 2. add an ability to autodetect if kernel has a support before running the
> test else skip test_invalid_dma_vector_length() ?
It already does the second one. This test function checks the device's
dma alignment requirements and purposefully dispatches a command that is
expected to fail before it reaches this part.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-19 19:54 ` [PATCHv2 1/2] blktests: test direct io offsets Keith Busch
2025-11-19 23:39 ` Chaitanya Kulkarni
2025-11-19 23:48 ` Chaitanya Kulkarni
@ 2025-11-25 11:26 ` Shinichiro Kawasaki
2025-11-25 16:42 ` Keith Busch
2 siblings, 1 reply; 12+ messages in thread
From: Shinichiro Kawasaki @ 2025-11-25 11:26 UTC (permalink / raw)
To: Keith Busch
Cc: linux-block@vger.kernel.org, chaitanyak@nvidia.com, Keith Busch
On Nov 19, 2025 / 11:54, Keith Busch wrote:
> From: Keith Busch <kbusch@kernel.org>
>
> Tests various direct IO memory and length alignments against the
> device's queue limits reported from sysfs.
>
> Signed-off-by: Keith Busch <kbusch@kernel.org>
> ---
[...]
> diff --git a/src/dio-offsets.c b/src/dio-offsets.c
> new file mode 100644
> index 0000000..8e46091
> --- /dev/null
> +++ b/src/dio-offsets.c
[...]
> +static void init_buffers()
> +{
> + unsigned long lb_mask = logical_block_size - 1;
> + int fd, ret;
> +
> + buf_size = max_bytes * max_segments / 2;
> + if (buf_size < logical_block_size * max_segments)
> + err(EINVAL, "%s: logical block size is too big", __func__);
> +
> + if (buf_size < logical_block_size * 1024 * 4)
> + buf_size = logical_block_size * 1024 * 4;
> +
> + if (buf_size & lb_mask)
> + buf_size = (buf_size + lb_mask) & ~(lb_mask);
I investigated why this new test case fails with my QEMU SATA device and noticed
that the device has 128MiB size. The calculation above sets buf_size = 192MiB.
Then pwrite() in test_full_size_aligned() is called with 192MiB size and it was
truncated to 128MiB. Then the following compare() check failed.
Is it okay to cap the buf_size with the device size? If so, I suggest the change
below. With this change, the test case passes with the device. If you are okay
with the change, I can fold it in when I apply the patch.
-------------------- diff start -------------------
diff --git a/src/dio-offsets.c b/src/dio-offsets.c
index 8e46091..7e1aecd 100644
--- a/src/dio-offsets.c
+++ b/src/dio-offsets.c
@@ -22,6 +22,8 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
#define power_of_2(x) ((x) && !((x) & ((x) - 1)))
@@ -81,6 +83,7 @@ static void init_buffers()
{
unsigned long lb_mask = logical_block_size - 1;
int fd, ret;
+ unsigned long long dev_bytes;
buf_size = max_bytes * max_segments / 2;
if (buf_size < logical_block_size * max_segments)
@@ -92,6 +95,13 @@ static void init_buffers()
if (buf_size & lb_mask)
buf_size = (buf_size + lb_mask) & ~(lb_mask);
+ ret = ioctl(test_fd, BLKGETSIZE64, &dev_bytes);
+ if (ret < 0)
+ err(ret, "%s: ioctl BLKGETSIZE64 failed", __func__);
+
+ if (dev_bytes < buf_size)
+ buf_size = dev_bytes;
+
ret = posix_memalign((void **)&in_buf, pagesize, buf_size);
if (ret)
err(EINVAL, "%s: failed to allocate in-buf", __func__);
-------------------- diff end -------------------
> +
> + ret = posix_memalign((void **)&in_buf, pagesize, buf_size);
> + if (ret)
> + err(EINVAL, "%s: failed to allocate in-buf", __func__);
> +
> + ret = posix_memalign((void **)&out_buf, pagesize, buf_size);
> + if (ret)
> + err(EINVAL, "%s: failed to allocate out-buf", __func__);
> +
> + fd = open("/dev/urandom", O_RDONLY);
> + if (fd < 0)
> + err(EINVAL, "%s: failed to open urandom", __func__);
> +
> + ret = read(fd, out_buf, buf_size);
> + if (ret < 0)
> + err(EINVAL, "%s: failed to randomize output buffer", __func__);
> +
> + close(fd);
> +}
[...]
> +/* ./$prog-name file */
> +int main(int argc, char **argv)
> +{
> + if (argc < 2)
> + errx(EINVAL, "expect argments: file");
> +
> + init_args(argv);
> + init_buffers();
> + run_tests();
> + close(test_fd);
> + free(out_buf);
> + free(in_buf);
> +
> + return 0;
Nit: last "}" is missing here. I can add it when I apply this patch.
> diff --git a/tests/block/042 b/tests/block/042
> new file mode 100644
> index 0000000..a911d82
> --- /dev/null
> +++ b/tests/block/042
> @@ -0,0 +1,26 @@
> +#!/bin/bash
Is it okay to add any GPL SPDX license and your copyright here? It is not ideal
that I add your copyright and license, but if you specify them in this thread, I
will add the Link to this thread in the commit message and fold-in the specifed
SPDX license and your copyright.
> +
> +. tests/block/rc
> +
> +DESCRIPTION="Test unusual direct-io offsets"
> +QUICK=1
> +
> +device_requires() {
> + _require_test_dev_sysfs
> +}
> +
> +test_device() {
> + echo "Running ${TEST_NAME}"
> +
> + sys_max_segments=$(cat "${TEST_DEV_SYSFS}/queue/max_segments")
> + sys_dma_alignment=$(cat "${TEST_DEV_SYSFS}/queue/dma_alignment")
> + sys_virt_boundary_mask=$(cat "${TEST_DEV_SYSFS}/queue/virt_boundary_mask")
> + sys_logical_block_size=$(cat "${TEST_DEV_SYSFS}/queue/logical_block_size")
> + sys_max_sectors_kb=$(cat "${TEST_DEV_SYSFS}/queue/max_sectors_kb")
> +
> + if ! src/dio-offsets ${TEST_DEV} $sys_max_segments $sys_max_sectors_kb $sys_dma_alignment $sys_virt_boundary_mask $sys_logical_block_size; then
> + echo "src/dio-offsets failed"
> + fi
> +
> + echo "Test complete"
> +}
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-25 11:26 ` Shinichiro Kawasaki
@ 2025-11-25 16:42 ` Keith Busch
2025-12-02 10:22 ` Shinichiro Kawasaki
0 siblings, 1 reply; 12+ messages in thread
From: Keith Busch @ 2025-11-25 16:42 UTC (permalink / raw)
To: Shinichiro Kawasaki
Cc: Keith Busch, linux-block@vger.kernel.org, chaitanyak@nvidia.com
On Tue, Nov 25, 2025 at 11:26:31AM +0000, Shinichiro Kawasaki wrote:
> On Nov 19, 2025 / 11:54, Keith Busch wrote:
> > From: Keith Busch <kbusch@kernel.org>
> >
> > Tests various direct IO memory and length alignments against the
> > device's queue limits reported from sysfs.
> >
> > Signed-off-by: Keith Busch <kbusch@kernel.org>
> > ---
> [...]
> > diff --git a/src/dio-offsets.c b/src/dio-offsets.c
> > new file mode 100644
> > index 0000000..8e46091
> > --- /dev/null
> > +++ b/src/dio-offsets.c
> [...]
> > +static void init_buffers()
> > +{
> > + unsigned long lb_mask = logical_block_size - 1;
> > + int fd, ret;
> > +
> > + buf_size = max_bytes * max_segments / 2;
> > + if (buf_size < logical_block_size * max_segments)
> > + err(EINVAL, "%s: logical block size is too big", __func__);
> > +
> > + if (buf_size < logical_block_size * 1024 * 4)
> > + buf_size = logical_block_size * 1024 * 4;
> > +
> > + if (buf_size & lb_mask)
> > + buf_size = (buf_size + lb_mask) & ~(lb_mask);
>
> I investigated why this new test case fails with my QEMU SATA device and noticed
> that the device has 128MiB size. The calculation above sets buf_size = 192MiB.
> Then pwrite() in test_full_size_aligned() is called with 192MiB size and it was
> truncated to 128MiB. Then the following compare() check failed.
>
> Is it okay to cap the buf_size with the device size? If so, I suggest the change
> below. With this change, the test case passes with the device. If you are okay
> with the change, I can fold it in when I apply the patch.
Yeah, totally fine. Much of the sizes were just made up as I went along,
but it should probably be programatically calculated based on the device
under test.
> > +#!/bin/bash
>
> Is it okay to add any GPL SPDX license and your copyright here? It is not ideal
> that I add your copyright and license, but if you specify them in this thread, I
> will add the Link to this thread in the commit message and fold-in the specifed
> SPDX license and your copyright.
Yes, sorry, it should have been there. I added those to some files, but
missed this one.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv2 1/2] blktests: test direct io offsets
2025-11-25 16:42 ` Keith Busch
@ 2025-12-02 10:22 ` Shinichiro Kawasaki
0 siblings, 0 replies; 12+ messages in thread
From: Shinichiro Kawasaki @ 2025-12-02 10:22 UTC (permalink / raw)
To: Keith Busch
Cc: Keith Busch, linux-block@vger.kernel.org, chaitanyak@nvidia.com
On Nov 25, 2025 / 09:42, Keith Busch wrote:
> On Tue, Nov 25, 2025 at 11:26:31AM +0000, Shinichiro Kawasaki wrote:
> > On Nov 19, 2025 / 11:54, Keith Busch wrote:
> > > From: Keith Busch <kbusch@kernel.org>
> > >
> > > Tests various direct IO memory and length alignments against the
> > > device's queue limits reported from sysfs.
> > >
> > > Signed-off-by: Keith Busch <kbusch@kernel.org>
> > > ---
> > [...]
> > > diff --git a/src/dio-offsets.c b/src/dio-offsets.c
> > > new file mode 100644
> > > index 0000000..8e46091
> > > --- /dev/null
> > > +++ b/src/dio-offsets.c
> > [...]
> > > +static void init_buffers()
> > > +{
> > > + unsigned long lb_mask = logical_block_size - 1;
> > > + int fd, ret;
> > > +
> > > + buf_size = max_bytes * max_segments / 2;
> > > + if (buf_size < logical_block_size * max_segments)
> > > + err(EINVAL, "%s: logical block size is too big", __func__);
> > > +
> > > + if (buf_size < logical_block_size * 1024 * 4)
> > > + buf_size = logical_block_size * 1024 * 4;
> > > +
> > > + if (buf_size & lb_mask)
> > > + buf_size = (buf_size + lb_mask) & ~(lb_mask);
> >
> > I investigated why this new test case fails with my QEMU SATA device and noticed
> > that the device has 128MiB size. The calculation above sets buf_size = 192MiB.
> > Then pwrite() in test_full_size_aligned() is called with 192MiB size and it was
> > truncated to 128MiB. Then the following compare() check failed.
> >
> > Is it okay to cap the buf_size with the device size? If so, I suggest the change
> > below. With this change, the test case passes with the device. If you are okay
> > with the change, I can fold it in when I apply the patch.
>
> Yeah, totally fine. Much of the sizes were just made up as I went along,
> but it should probably be programatically calculated based on the device
> under test.
>
> > > +#!/bin/bash
> >
> > Is it okay to add any GPL SPDX license and your copyright here? It is not ideal
> > that I add your copyright and license, but if you specify them in this thread, I
> > will add the Link to this thread in the commit message and fold-in the specifed
> > SPDX license and your copyright.
>
> Yes, sorry, it should have been there. I added those to some files, but
> missed this one.
Thanks for the confirmations. I have applied the patches. I took the liberty to
make follow-up commits for minor clean-ups, hoping they are fine for you.
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-12-02 10:22 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-19 19:54 [PATCHv2 0/2] blktests: add tests with offsets Keith Busch
2025-11-19 19:54 ` [PATCHv2 1/2] blktests: test direct io offsets Keith Busch
2025-11-19 23:39 ` Chaitanya Kulkarni
2025-11-19 23:48 ` Chaitanya Kulkarni
2025-11-19 23:59 ` Keith Busch
2025-11-20 0:56 ` Chaitanya Kulkarni
2025-11-20 14:32 ` Keith Busch
2025-11-25 11:26 ` Shinichiro Kawasaki
2025-11-25 16:42 ` Keith Busch
2025-12-02 10:22 ` Shinichiro Kawasaki
2025-11-19 19:54 ` [PATCHv2 2/2] blktests: test io_uring user metadata offsets Keith Busch
2025-11-19 23:39 ` Chaitanya Kulkarni
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox