* [PATCH v5 00/11] Add more tests for multi fs block atomic writes
@ 2025-08-22 8:01 Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 01/12] common/rc: Add _min() and _max() helpers Ojaswin Mujoo
` (11 more replies)
0 siblings, 12 replies; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:01 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
Changes in v5: (Thanks to John & Darrick for reviews)
- commor/rc: Add a _require_fio_version helper
- fsx: Switch atomic writes off if direct IO (-Z) not passed
- fio tests: better commit messages to explain what we are testing
- ext4/06{1..2}: Refactor code, also test only a few combinations of bs
clustersize rather than every single
Changes in v4: (Thanks to Darrick, John and Zorro for the reviews) [4]
- g/1226,1227: Modify fio threads to not issue overlapping atomic writes
- g/1228: Use xfs_io -c "shutdown" instead of _scratch_shutdown to avoid
bash overhead
- g/1229: Remove FSX_AVOID handling for bigalloc from common/rc. It is
part of the specific test now
- ext4/063: add more clearer extent diagram
- ext4/064: Drop the test for now as im taking sometime to understand
the behavior better.
- Removed test numbers from commit message
- For tests with significant changes I've removed the RVBs
[4] https://lore.kernel.org/fstests/0eb2703b-a862-4a40-b271-6b8bb27b4ad4@oracle.com/T/#mef34a8c13cbee466bfc162db637d6e1cf0a8b06d
Changes in v3 [3]:
- (2/13) use dumpe2fs to figure out if FS is bigalloc
- (9/13) generic/1230: Detect device speeds for more accurate testing. ALso
speeds up the test
- fio tests - switch to write followed by verify approach to avoid false
failures due to fio verify reads splitting and racing with atomic
writes. Discussion thread:
https://lore.kernel.org/fstests/0430bd73-e6c2-4ce9-af24-67b1e1fa9b5b@oracle.com/
[3] https://lore.kernel.org/fstests/cover.1752329098.git.ojaswin@linux.ibm.com/
Changes in v2 [1]:
- (1/13) new patch with _min and _max helpers
- (2/13) remove setup_fs_options and add fsx specifc helper
- (4/13) skip atomic write instead of falling back to normal write (fsx)
- (4/13) make atomic write default on instead of default off (fsx)
- (5,6/13) refactor and cleanup fio tests
- (7/13) refactored common code
- (8/13) dont ignore mmap writes for fsx with atomic writes
- (9/13) use od instead of xxd. handle cleanup of bg threads in _cleanup()
- (10-13/13) minor refactors
- change all tests use _fail for better consistency
- use higher tests numbers for easier merging
[1] https://lore.kernel.org/fstests/cover.1750924903.git.ojaswin@linux.ibm.com/
* Original cover [2] *
These are the tests we were using to verify that filesystems are not
tearing multi fs block atomic writes. Infact some of the tests like
generic/772 (now: g/1230) actually helped us catch and fix issues in
ext4's early implementations of multi fs block atomic writes and hence
we feel these tests are useful to have in xfstests.
We have tested these with scsi debug as well as a real nvme device
supporting multi fs block atomic writes.
Thoughts and suggestions are welcome!
[2] rfc: https://lore.kernel.org/fstests/cover.1749629233.git.ojaswin@linux.ibm.com/
Ojaswin Mujoo (10):
common/rc: Add _min() and _max() helpers
common/rc: Add _require_fio_version helper
common/rc: Add a helper to run fsx on a given file
ltp/fsx.c: Add atomic writes support to fsx
generic: Add atomic write test using fio crc check verifier
generic: Add atomic write test using fio verify on file mixed mappings
generic: Add atomic write multi-fsblock O_[D]SYNC tests
generic: Stress fsx with atomic writes enabled
generic: Add sudden shutdown tests for multi block atomic writes
ext4: Atomic write test for extent split across leaf nodes
Ritesh Harjani (IBM) (2):
ext4: test atomic write and ioend codepaths with bigalloc
ext4: Test atomic writes allocation and write codepaths with bigalloc
common/rc | 77 +++++++-
ltp/fsx.c | 115 +++++++++++-
tests/ext4/061 | 155 ++++++++++++++++
tests/ext4/061.out | 2 +
tests/ext4/062 | 203 +++++++++++++++++++++
tests/ext4/062.out | 2 +
tests/ext4/063 | 129 +++++++++++++
tests/ext4/063.out | 2 +
tests/generic/1226 | 108 +++++++++++
tests/generic/1226.out | 2 +
tests/generic/1227 | 132 ++++++++++++++
tests/generic/1227.out | 2 +
tests/generic/1228 | 137 ++++++++++++++
tests/generic/1228.out | 2 +
tests/generic/1229 | 68 +++++++
tests/generic/1229.out | 2 +
tests/generic/1230 | 397 +++++++++++++++++++++++++++++++++++++++++
tests/generic/1230.out | 2 +
18 files changed, 1529 insertions(+), 8 deletions(-)
create mode 100755 tests/ext4/061
create mode 100644 tests/ext4/061.out
create mode 100755 tests/ext4/062
create mode 100644 tests/ext4/062.out
create mode 100755 tests/ext4/063
create mode 100644 tests/ext4/063.out
create mode 100755 tests/generic/1226
create mode 100644 tests/generic/1226.out
create mode 100755 tests/generic/1227
create mode 100644 tests/generic/1227.out
create mode 100755 tests/generic/1228
create mode 100644 tests/generic/1228.out
create mode 100755 tests/generic/1229
create mode 100644 tests/generic/1229.out
create mode 100755 tests/generic/1230
create mode 100644 tests/generic/1230.out
--
2.49.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v5 01/12] common/rc: Add _min() and _max() helpers
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 02/12] common/rc: Add _require_fio_version helper Ojaswin Mujoo
` (10 subsequent siblings)
11 siblings, 0 replies; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
Many programs open code these functionalities so add it as a generic helper
in common/rc
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
common/rc | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/common/rc b/common/rc
index ff5df203..35a1c835 100644
--- a/common/rc
+++ b/common/rc
@@ -5975,6 +5975,28 @@ _require_inplace_writes()
fi
}
+_min() {
+ local ret
+
+ for arg in "$@"; do
+ if [ -z "$ret" ] || (( $arg < $ret )); then
+ ret="$arg"
+ fi
+ done
+ echo $ret
+}
+
+_max() {
+ local ret
+
+ for arg in "$@"; do
+ if [ -z "$ret" ] || (( $arg > $ret )); then
+ ret="$arg"
+ fi
+ done
+ echo $ret
+}
+
################################################################################
# make sure this script returns success
/bin/true
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 01/12] common/rc: Add _min() and _max() helpers Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-08-25 16:08 ` Zorro Lang
2025-09-02 14:50 ` John Garry
2025-08-22 8:02 ` [PATCH v5 03/12] common/rc: Add a helper to run fsx on a given file Ojaswin Mujoo
` (9 subsequent siblings)
11 siblings, 2 replies; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
The main motivation of adding this function on top of _require_fio is
that there has been a case in fio where atomic= option was added but
later it was changed to noop since kernel didn't yet have support for
atomic writes. It was then again utilized to do atomic writes in a later
version, once kernel got the support. Due to this there is a point in
fio where _require_fio w/ atomic=1 will succeed even though it would
not be doing atomic writes.
Hence, add an explicit helper to ensure tests to require specific
versions of fio to work past such issues.
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
common/rc | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/common/rc b/common/rc
index 35a1c835..f45b9a38 100644
--- a/common/rc
+++ b/common/rc
@@ -5997,6 +5997,38 @@ _max() {
echo $ret
}
+# Check the required fio version. Examples:
+# _require_fio_version 3.38 (matches 3.38 only)
+# _require_fio_version 3.38+ (matches 3.38 and above)
+# _require_fio_version 3.38- (matches 3.38 and below)
+_require_fio_version() {
+ local req_ver="$1"
+ local fio_ver
+
+ _require_fio
+ _require_math
+
+ fio_ver=$(fio -v | cut -d"-" -f2)
+
+ case "$req_ver" in
+ *+)
+ req_ver=${req_ver%+}
+ test $(_math "$fio_ver >= $req_ver") -eq 1 || \
+ _notrun "need fio >= $req_ver (found $fio_ver)"
+ ;;
+ *-)
+ req_ver=${req_ver%-}
+ test $(_math "$fio_ver <= $req_ver") -eq 1 || \
+ _notrun "need fio <= $req_ver (found $fio_ver)"
+ ;;
+ *)
+ req_ver=${req_ver%-}
+ test $(_math "$fio_ver == $req_ver") -eq 1 || \
+ _notrun "need fio = $req_ver (found $fio_ver)"
+ ;;
+ esac
+}
+
################################################################################
# make sure this script returns success
/bin/true
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 03/12] common/rc: Add a helper to run fsx on a given file
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 01/12] common/rc: Add _min() and _max() helpers Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 02/12] common/rc: Add _require_fio_version helper Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 04/12] ltp/fsx.c: Add atomic writes support to fsx Ojaswin Mujoo
` (8 subsequent siblings)
11 siblings, 0 replies; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
Currently run_fsx is hardcoded to run on a file in $TEST_DIR.
Add a helper _run_fsx_on_file so that we can run fsx on any
given file including in $SCRATCH_MNT. Also, refactor _run_fsx
to use this helper.
No functional change is intended in this patch.
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
common/rc | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/common/rc b/common/rc
index f45b9a38..85baa677 100644
--- a/common/rc
+++ b/common/rc
@@ -5200,13 +5200,24 @@ _require_hugepage_fsx()
_notrun "fsx binary does not support MADV_COLLAPSE"
}
-_run_fsx()
+_run_fsx_on_file()
{
+ local testfile=$1
+ shift
+
+ if ! [ -f $testfile ]
+ then
+ echo "_run_fsx_on_file: $testfile doesn't exist. Creating" >> $seqres.full
+ touch $testfile
+ fi
+
echo "fsx $*"
local args=`echo $@ | sed -e "s/ BSIZE / $bsize /g" -e "s/ PSIZE / $psize /g"`
- set -- $FSX_PROG $args $FSX_AVOID $TEST_DIR/junk
+
+ set -- $FSX_PROG $args $FSX_AVOID $testfile
+
echo "$@" >>$seqres.full
- rm -f $TEST_DIR/junk
+ rm -f $testfile
"$@" 2>&1 | tee -a $seqres.full >$tmp.fsx
local res=${PIPESTATUS[0]}
if [ $res -ne 0 ]; then
@@ -5218,6 +5229,12 @@ _run_fsx()
return 0
}
+_run_fsx()
+{
+ _run_fsx_on_file $TEST_DIR/junk $@
+ return $?
+}
+
# Run fsx with -h(ugepage buffers). If we can't set up a hugepage then skip
# the test, but if any other error occurs then exit the test.
_run_hugepage_fsx() {
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 04/12] ltp/fsx.c: Add atomic writes support to fsx
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (2 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 03/12] common/rc: Add a helper to run fsx on a given file Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-09-02 15:06 ` John Garry
2025-08-22 8:02 ` [PATCH v5 05/12] generic: Add atomic write test using fio crc check verifier Ojaswin Mujoo
` (7 subsequent siblings)
11 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
Implement atomic write support to help fuzz atomic writes
with fsx.
Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
ltp/fsx.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 110 insertions(+), 5 deletions(-)
diff --git a/ltp/fsx.c b/ltp/fsx.c
index 163b9453..1582f6d1 100644
--- a/ltp/fsx.c
+++ b/ltp/fsx.c
@@ -40,6 +40,7 @@
#include <liburing.h>
#endif
#include <sys/syscall.h>
+#include "statx.h"
#ifndef MAP_FILE
# define MAP_FILE 0
@@ -49,6 +50,10 @@
#define RWF_DONTCACHE 0x80
#endif
+#ifndef RWF_ATOMIC
+#define RWF_ATOMIC 0x40
+#endif
+
#define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */
/* Operation flags (bitmask) */
@@ -110,6 +115,7 @@ enum {
OP_READ_DONTCACHE,
OP_WRITE,
OP_WRITE_DONTCACHE,
+ OP_WRITE_ATOMIC,
OP_MAPREAD,
OP_MAPWRITE,
OP_MAX_LITE,
@@ -200,6 +206,11 @@ int uring = 0;
int mark_nr = 0;
int dontcache_io = 1;
int hugepages = 0; /* -h flag */
+int do_atomic_writes = 1; /* -a flag disables */
+
+/* User for atomic writes */
+int awu_min = 0;
+int awu_max = 0;
/* Stores info needed to periodically collapse hugepages */
struct hugepages_collapse_info {
@@ -288,6 +299,7 @@ static const char *op_names[] = {
[OP_READ_DONTCACHE] = "read_dontcache",
[OP_WRITE] = "write",
[OP_WRITE_DONTCACHE] = "write_dontcache",
+ [OP_WRITE_ATOMIC] = "write_atomic",
[OP_MAPREAD] = "mapread",
[OP_MAPWRITE] = "mapwrite",
[OP_TRUNCATE] = "truncate",
@@ -422,6 +434,7 @@ logdump(void)
prt("\t***RRRR***");
break;
case OP_WRITE_DONTCACHE:
+ case OP_WRITE_ATOMIC:
case OP_WRITE:
prt("WRITE 0x%x thru 0x%x\t(0x%x bytes)",
lp->args[0], lp->args[0] + lp->args[1] - 1,
@@ -1073,6 +1086,25 @@ update_file_size(unsigned offset, unsigned size)
file_size = offset + size;
}
+static int is_power_of_2(unsigned n) {
+ return ((n & (n - 1)) == 0);
+}
+
+/*
+ * Round down n to nearest power of 2.
+ * If n is already a power of 2, return n;
+ */
+static int rounddown_pow_of_2(int n) {
+ int i = 0;
+
+ if (is_power_of_2(n))
+ return n;
+
+ for (; (1 << i) < n; i++);
+
+ return 1 << (i - 1);
+}
+
void
dowrite(unsigned offset, unsigned size, int flags)
{
@@ -1081,6 +1113,27 @@ dowrite(unsigned offset, unsigned size, int flags)
offset -= offset % writebdy;
if (o_direct)
size -= size % writebdy;
+ if (flags & RWF_ATOMIC) {
+ /* atomic write len must be inbetween awu_min and awu_max */
+ if (size < awu_min)
+ size = awu_min;
+ if (size > awu_max)
+ size = awu_max;
+
+ /* atomic writes need power-of-2 sizes */
+ size = rounddown_pow_of_2(size);
+
+ /* atomic writes need naturally aligned offsets */
+ offset -= offset % size;
+
+ /* Skip the write if we are crossing max filesize */
+ if ((offset + size) > maxfilelen) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping atomic write past maxfilelen\n");
+ log4(OP_WRITE_ATOMIC, offset, size, FL_SKIPPED);
+ return;
+ }
+ }
if (size == 0) {
if (!quiet && testcalls > simulatedopcount && !o_direct)
prt("skipping zero size write\n");
@@ -1088,7 +1141,10 @@ dowrite(unsigned offset, unsigned size, int flags)
return;
}
- log4(OP_WRITE, offset, size, FL_NONE);
+ if (flags & RWF_ATOMIC)
+ log4(OP_WRITE_ATOMIC, offset, size, FL_NONE);
+ else
+ log4(OP_WRITE, offset, size, FL_NONE);
gendata(original_buf, good_buf, offset, size);
if (offset + size > file_size) {
@@ -1108,8 +1164,9 @@ dowrite(unsigned offset, unsigned size, int flags)
(monitorstart == -1 ||
(offset + size > monitorstart &&
(monitorend == -1 || offset <= monitorend))))))
- prt("%lld write\t0x%x thru\t0x%x\t(0x%x bytes)\tdontcache=%d\n", testcalls,
- offset, offset + size - 1, size, (flags & RWF_DONTCACHE) != 0);
+ prt("%lld write\t0x%x thru\t0x%x\t(0x%x bytes)\tdontcache=%d atomic_wr=%d\n", testcalls,
+ offset, offset + size - 1, size, (flags & RWF_DONTCACHE) != 0,
+ (flags & RWF_ATOMIC) != 0);
iret = fsxwrite(fd, good_buf + offset, size, offset, flags);
if (iret != size) {
if (iret == -1)
@@ -1785,6 +1842,36 @@ do_dedupe_range(unsigned offset, unsigned length, unsigned dest)
}
#endif
+int test_atomic_writes(void) {
+ int ret;
+ struct statx stx;
+
+ if (o_direct != O_DIRECT) {
+ fprintf(stderr, "main: atomic writes need O_DIRECT (-Z), "
+ "disabling!\n");
+ return 0;
+ }
+
+ ret = xfstests_statx(AT_FDCWD, fname, 0, STATX_WRITE_ATOMIC, &stx);
+ if (ret < 0) {
+ fprintf(stderr, "main: Statx failed with %d."
+ " Failed to determine atomic write limits, "
+ " disabling!\n", ret);
+ return 0;
+ }
+
+ if (stx.stx_attributes & STATX_ATTR_WRITE_ATOMIC &&
+ stx.stx_atomic_write_unit_min > 0) {
+ awu_min = stx.stx_atomic_write_unit_min;
+ awu_max = stx.stx_atomic_write_unit_max;
+ return 1;
+ }
+
+ fprintf(stderr, "main: IO Stack does not support "
+ "atomic writes, disabling!\n");
+ return 0;
+}
+
#ifdef HAVE_COPY_FILE_RANGE
int
test_copy_range(void)
@@ -2356,6 +2443,12 @@ have_op:
goto out;
}
break;
+ case OP_WRITE_ATOMIC:
+ if (!do_atomic_writes) {
+ log4(OP_WRITE_ATOMIC, offset, size, FL_SKIPPED);
+ goto out;
+ }
+ break;
}
switch (op) {
@@ -2385,6 +2478,11 @@ have_op:
dowrite(offset, size, 0);
break;
+ case OP_WRITE_ATOMIC:
+ TRIM_OFF_LEN(offset, size, maxfilelen);
+ dowrite(offset, size, RWF_ATOMIC);
+ break;
+
case OP_MAPREAD:
TRIM_OFF_LEN(offset, size, file_size);
domapread(offset, size);
@@ -2511,13 +2609,14 @@ void
usage(void)
{
fprintf(stdout, "usage: %s",
- "fsx [-dfhknqxyzBEFHIJKLORWXZ0]\n\
+ "fsx [-adfhknqxyzBEFHIJKLORWXZ0]\n\
[-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid]\n\
[-l flen] [-m start:end] [-o oplen] [-p progressinterval]\n\
[-r readbdy] [-s style] [-t truncbdy] [-w writebdy]\n\
[-A|-U] [-D startingop] [-N numops] [-P dirpath] [-S seed]\n\
[--replay-ops=opsfile] [--record-ops[=opsfile]] [--duration=seconds]\n\
... fname\n\
+ -a: disable atomic writes\n\
-b opnum: beginning operation number (default 1)\n\
-c P: 1 in P chance of file close+open at each op (default infinity)\n\
-d: debug output for all operations\n\
@@ -3059,9 +3158,13 @@ main(int argc, char **argv)
setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
while ((ch = getopt_long(argc, argv,
- "0b:c:de:fg:hi:j:kl:m:no:p:qr:s:t:uw:xyABD:EFJKHzCILN:OP:RS:UWXZ",
+ "0ab:c:de:fg:hi:j:kl:m:no:p:qr:s:t:uw:xyABD:EFJKHzCILN:OP:RS:UWXZ",
longopts, NULL)) != EOF)
switch (ch) {
+ case 'a':
+ prt("main(): Atomic writes disabled\n");
+ do_atomic_writes = 0;
+ break;
case 'b':
simulatedopcount = getnum(optarg, &endp);
if (!quiet)
@@ -3475,6 +3578,8 @@ main(int argc, char **argv)
exchange_range_calls = test_exchange_range();
if (dontcache_io)
dontcache_io = test_dontcache_io();
+ if (do_atomic_writes)
+ do_atomic_writes = test_atomic_writes();
while (keep_running())
if (!test())
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 05/12] generic: Add atomic write test using fio crc check verifier
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (3 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 04/12] ltp/fsx.c: Add atomic writes support to fsx Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-09-02 15:09 ` John Garry
2025-08-22 8:02 ` [PATCH v5 06/12] generic: Add atomic write test using fio verify on file mixed mappings Ojaswin Mujoo
` (6 subsequent siblings)
11 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
This adds atomic write test using fio based on it's crc check verifier.
fio adds a crc header for each data block, which is verified later to
ensure there is no data corruption or torn write.
This test essentially does a lot of parallel RWF_ATOMIC IO on a
preallocated file to stress the write and end-io unwritten conversion
code paths. The idea is to increase code coverage to ensure RWF_ATOMIC
hasn't introduced any issues.
Avoid doing overlapping parallel atomic writes because it might give
unexpected results. Use offset_increment=, size= fio options to achieve
this behavior.
Co-developed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/generic/1226 | 108 +++++++++++++++++++++++++++++++++++++++++
tests/generic/1226.out | 2 +
2 files changed, 110 insertions(+)
create mode 100755 tests/generic/1226
create mode 100644 tests/generic/1226.out
diff --git a/tests/generic/1226 b/tests/generic/1226
new file mode 100755
index 00000000..4584f062
--- /dev/null
+++ b/tests/generic/1226
@@ -0,0 +1,108 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test 1226
+#
+# Validate FS atomic write using fio crc check verifier.
+#
+. ./common/preamble
+. ./common/atomicwrites
+
+_begin_fstest auto aio rw atomicwrites
+
+_require_scratch_write_atomic
+_require_odirect
+_require_aio
+_require_fio_version "3.38+"
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+_require_xfs_io_command "falloc"
+
+touch "$SCRATCH_MNT/f1"
+awu_min_write=$(_get_atomic_write_unit_min "$SCRATCH_MNT/f1")
+awu_max_write=$(_get_atomic_write_unit_max "$SCRATCH_MNT/f1")
+
+blocksize=$(_max "$awu_min_write" "$((awu_max_write/2))")
+threads=$(_min "$(($(nproc) * 2 * LOAD_FACTOR))" "100")
+filesize=$((blocksize * threads * 100))
+depth=$threads
+io_size=$((filesize / threads))
+io_inc=$io_size
+testfile=$SCRATCH_MNT/test-file
+
+fio_config=$tmp.fio
+fio_out=$tmp.fio.out
+
+fio_aw_config=$tmp.aw.fio
+fio_verify_config=$tmp.verify.fio
+
+function create_fio_configs()
+{
+ create_fio_aw_config
+ create_fio_verify_config
+}
+
+function create_fio_verify_config()
+{
+cat >$fio_verify_config <<EOF
+ [verify-job]
+ direct=1
+ ioengine=libaio
+ rw=read
+ bs=$blocksize
+ filename=$testfile
+ size=$filesize
+ iodepth=$depth
+ group_reporting=1
+
+ verify_only=1
+ verify=crc32c
+ verify_fatal=1
+ verify_state_save=0
+ verify_write_sequence=0
+EOF
+}
+
+function create_fio_aw_config()
+{
+cat >$fio_aw_config <<EOF
+ [atomicwrite-job]
+ direct=1
+ ioengine=libaio
+ rw=randwrite
+ bs=$blocksize
+ filename=$testfile
+ size=$io_inc
+ offset_increment=$io_inc
+ iodepth=$depth
+ numjobs=$threads
+ group_reporting=1
+ atomic=1
+
+ verify_state_save=0
+ verify=crc32c
+ do_verify=0
+EOF
+}
+
+create_fio_configs
+_require_fio $fio_aw_config
+
+cat $fio_aw_config >> $seqres.full
+cat $fio_verify_config >> $seqres.full
+
+$XFS_IO_PROG -fc "falloc 0 $filesize" $testfile >> $seqres.full
+
+$FIO_PROG $fio_aw_config >> $seqres.full
+ret1=$?
+$FIO_PROG $fio_verify_config >> $seqres.full
+ret2=$?
+
+[[ $ret1 -eq 0 && $ret2 -eq 0 ]] || _fail "fio with atomic write failed"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/generic/1226.out b/tests/generic/1226.out
new file mode 100644
index 00000000..6dce0ea5
--- /dev/null
+++ b/tests/generic/1226.out
@@ -0,0 +1,2 @@
+QA output created by 1226
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 06/12] generic: Add atomic write test using fio verify on file mixed mappings
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (4 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 05/12] generic: Add atomic write test using fio crc check verifier Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-09-02 15:10 ` John Garry
2025-08-22 8:02 ` [PATCH v5 07/12] generic: Add atomic write multi-fsblock O_[D]SYNC tests Ojaswin Mujoo
` (5 subsequent siblings)
11 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
This test uses fio to first create a file with mixed mappings. Then it
does atomic writes using aio dio with parallel jobs to the same file
with mixed mappings. Finally, we perform a fio verify step to ensure
there is no data corruption or torn write.
The aim is to stress the FS block allocation and extent handling logic
to ensure it handles mixed mappings with RWF_ATOMIC correctly without
tearing or losing data.
Avoid doing overlapping parallel atomic writes because it might give
unexpected results. Use offset_increment=, size= fio options to achieve
this behavior.
Co-developed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/generic/1227 | 132 +++++++++++++++++++++++++++++++++++++++++
tests/generic/1227.out | 2 +
2 files changed, 134 insertions(+)
create mode 100755 tests/generic/1227
create mode 100644 tests/generic/1227.out
diff --git a/tests/generic/1227 b/tests/generic/1227
new file mode 100755
index 00000000..a5ff9fca
--- /dev/null
+++ b/tests/generic/1227
@@ -0,0 +1,132 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test 1227
+#
+# Validate FS atomic write using fio crc check verifier on mixed mappings
+# of a file.
+#
+. ./common/preamble
+. ./common/atomicwrites
+
+_begin_fstest auto aio rw atomicwrites
+
+_require_scratch_write_atomic_multi_fsblock
+_require_odirect
+_require_aio
+_require_fio_version "3.38+"
+_require_xfs_io_command "truncate"
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+touch "$SCRATCH_MNT/f1"
+awu_min_write=$(_get_atomic_write_unit_min "$SCRATCH_MNT/f1")
+awu_max_write=$(_get_atomic_write_unit_max "$SCRATCH_MNT/f1")
+
+aw_bsize=$(_max "$awu_min_write" "$((awu_max_write/4))")
+fsbsize=$(_get_block_size $SCRATCH_MNT)
+
+threads=$(_min "$(($(nproc) * 2 * LOAD_FACTOR))" "100")
+filesize=$((aw_bsize * threads * 100))
+depth=$threads
+aw_io_size=$((filesize / threads))
+aw_io_inc=$aw_io_size
+testfile=$SCRATCH_MNT/test-file
+
+fio_prep_config=$tmp.prep.fio
+fio_aw_config=$tmp.aw.fio
+fio_verify_config=$tmp.verify.fio
+fio_out=$tmp.fio.out
+
+cat >$fio_prep_config <<EOF
+# prep file to have mixed mappings
+[global]
+ioengine=libaio
+filename=$testfile
+size=$filesize
+bs=$fsbsize
+direct=1
+iodepth=$depth
+group_reporting=1
+
+# Create written extents
+[prep_written_blocks]
+ioengine=libaio
+rw=randwrite
+io_size=$((filesize/3))
+random_generator=lfsr
+
+# Create unwritten extents
+[prep_unwritten_blocks]
+ioengine=falloc
+rw=randwrite
+io_size=$((filesize/3))
+random_generator=lfsr
+EOF
+
+cat >$fio_aw_config <<EOF
+# atomic write to mixed mappings of written/unwritten/holes
+[atomic_write_job]
+ioengine=libaio
+rw=randwrite
+direct=1
+atomic=1
+random_generator=lfsr
+group_reporting=1
+
+filename=$testfile
+bs=$aw_bsize
+size=$aw_io_size
+offset_increment=$aw_io_inc
+iodepth=$depth
+numjobs=$threads
+
+verify_state_save=0
+verify=crc32c
+do_verify=0
+EOF
+
+cat >$fio_verify_config <<EOF
+# verify atomic writes done by previous job
+[verify_job]
+ioengine=libaio
+rw=read
+random_generator=lfsr
+group_reporting=1
+
+filename=$testfile
+size=$filesize
+bs=$aw_bsize
+iodepth=$depth
+
+verify_state_save=0
+verify_only=1
+verify=crc32c
+verify_fatal=1
+verify_write_sequence=0
+EOF
+
+_require_fio $fio_aw_config
+_require_fio $fio_verify_config
+
+cat $fio_prep_config >> $seqres.full
+cat $fio_aw_config >> $seqres.full
+cat $fio_verify_config >> $seqres.full
+
+$XFS_IO_PROG -fc "truncate $filesize" $testfile >> $seqres.full
+
+#prepare file with mixed mappings
+$FIO_PROG $fio_prep_config >> $seqres.full
+
+# do atomic writes without verifying
+$FIO_PROG $fio_aw_config >> $seqres.full
+
+# verify data is not torn
+$FIO_PROG $fio_verify_config >> $seqres.full
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/generic/1227.out b/tests/generic/1227.out
new file mode 100644
index 00000000..2605d062
--- /dev/null
+++ b/tests/generic/1227.out
@@ -0,0 +1,2 @@
+QA output created by 1227
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 07/12] generic: Add atomic write multi-fsblock O_[D]SYNC tests
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (5 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 06/12] generic: Add atomic write test using fio verify on file mixed mappings Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-09-02 15:14 ` John Garry
2025-08-22 8:02 ` [PATCH v5 08/12] generic: Stress fsx with atomic writes enabled Ojaswin Mujoo
` (4 subsequent siblings)
11 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
This adds various atomic write multi-fsblock stresst tests
with mixed mappings and O_SYNC, to ensure the data and metadata
is atomically persisted even if there is a shutdown.
Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/generic/1228 | 137 +++++++++++++++++++++++++++++++++++++++++
tests/generic/1228.out | 2 +
2 files changed, 139 insertions(+)
create mode 100755 tests/generic/1228
create mode 100644 tests/generic/1228.out
diff --git a/tests/generic/1228 b/tests/generic/1228
new file mode 100755
index 00000000..888599ce
--- /dev/null
+++ b/tests/generic/1228
@@ -0,0 +1,137 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test 1228
+#
+# Atomic write multi-fsblock data integrity tests with mixed mappings
+# and O_SYNC
+#
+. ./common/preamble
+. ./common/atomicwrites
+_begin_fstest auto quick rw atomicwrites
+
+_require_scratch_write_atomic_multi_fsblock
+_require_atomic_write_test_commands
+_require_scratch_shutdown
+_require_xfs_io_command "truncate"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+check_data_integrity() {
+ actual=$(_hexdump $testfile)
+ if [[ "$expected" != "$actual" ]]
+ then
+ echo "Integrity check failed"
+ echo "Integrity check failed" >> $seqres.full
+ echo "# Expected file contents:" >> $seqres.full
+ echo "$expected" >> $seqres.full
+ echo "# Actual file contents:" >> $seqres.full
+ echo "$actual" >> $seqres.full
+
+ _fail "Data integrity check failed. The atomic write was torn."
+ fi
+}
+
+prep_mixed_mapping() {
+ $XFS_IO_PROG -c "truncate 0" $testfile >> $seqres.full
+ local off=0
+ local mapping=""
+
+ local operations=("W" "H" "U")
+ local num_blocks=$((awu_max / blksz))
+ for ((i=0; i<num_blocks; i++)); do
+ local index=$((RANDOM % ${#operations[@]}))
+ local map="${operations[$index]}"
+ local mapping="${mapping}${map}"
+
+ case "$map" in
+ "W")
+ $XFS_IO_PROG -dc "pwrite -S 0x61 -b $blksz $off $blksz" $testfile > /dev/null
+ ;;
+ "H")
+ # No operation needed for hole
+ ;;
+ "U")
+ $XFS_IO_PROG -c "falloc $off $blksz" $testfile >> /dev/null
+ ;;
+ esac
+ off=$((off + blksz))
+ done
+
+ echo "+ + Mixed mapping prep done. Full mapping pattern: $mapping" >> $seqres.full
+
+ sync $testfile
+}
+
+verify_atomic_write() {
+ test $bytes_written -eq $awu_max || _fail "atomic write len=$awu_max assertion failed"
+ check_data_integrity
+}
+
+mixed_mapping_test() {
+ prep_mixed_mapping
+
+ echo -"+ + Performing O_DSYNC atomic write from 0 to $awu_max" >> $seqres.full
+ if [[ "$1" == "shutdown" ]]
+ then
+ bytes_written=$($XFS_IO_PROG -x -dc \
+ "pwrite -DA -V1 -b $awu_max 0 $awu_max" \
+ -c "shutdown" $testfile | grep wrote | \
+ awk -F'[/ ]' '{print $2}')
+ _scratch_cycle_mount >>$seqres.full 2>&1 || _fail "remount failed"
+ else
+ bytes_written=$($XFS_IO_PROG -dc \
+ "pwrite -DA -V1 -b $awu_max 0 $awu_max" $testfile | \
+ grep wrote | awk -F'[/ ]' '{print $2}')
+ fi
+
+ verify_atomic_write
+}
+
+testfile=$SCRATCH_MNT/testfile
+touch $testfile
+
+awu_max=$(_get_atomic_write_unit_max $testfile)
+blksz=$(_get_block_size $SCRATCH_MNT)
+
+# Create an expected pattern to compare with
+$XFS_IO_PROG -tc "pwrite -b $awu_max 0 $awu_max" $testfile >> $seqres.full
+expected=$(_hexdump $testfile)
+echo "# Expected file contents:" >> $seqres.full
+echo "$expected" >> $seqres.full
+echo >> $seqres.full
+
+echo "# Test 1: Do O_DSYNC atomic write on random mixed mapping:" >> $seqres.full
+echo >> $seqres.full
+for ((iteration=1; iteration<=10; iteration++)); do
+ echo "=== Mixed Mapping Test Iteration $iteration ===" >> $seqres.full
+
+ echo "+ Testing without shutdown..." >> $seqres.full
+ mixed_mapping_test
+ echo "Passed!" >> $seqres.full
+
+ echo "+ Testing with sudden shutdown..." >> $seqres.full
+ mixed_mapping_test "shutdown"
+ echo "Passed!" >> $seqres.full
+
+ echo "Iteration $iteration completed: OK" >> $seqres.full
+ echo >> $seqres.full
+done
+echo "# Test 1: Do O_SYNC atomic write on random mixed mapping (10 iterations): OK" >> $seqres.full
+
+
+echo >> $seqres.full
+echo "# Test 2: Do extending O_SYNC atomic writes: " >> $seqres.full
+bytes_written=$($XFS_IO_PROG -x -dstc "pwrite -A -V1 -b $awu_max 0 $awu_max" \
+ -c "shutdown" $testfile | grep wrote | awk -F'[/ ]' '{print $2}')
+_scratch_cycle_mount >>$seqres.full 2>&1 || _fail "remount failed"
+verify_atomic_write
+echo "# Test 2: Do extending O_SYNC atomic writes: OK" >> $seqres.full
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
+
diff --git a/tests/generic/1228.out b/tests/generic/1228.out
new file mode 100644
index 00000000..1baffa91
--- /dev/null
+++ b/tests/generic/1228.out
@@ -0,0 +1,2 @@
+QA output created by 1228
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 08/12] generic: Stress fsx with atomic writes enabled
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (6 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 07/12] generic: Add atomic write multi-fsblock O_[D]SYNC tests Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-09-02 15:18 ` John Garry
2025-08-22 8:02 ` [PATCH v5 09/12] generic: Add sudden shutdown tests for multi block atomic writes Ojaswin Mujoo
` (3 subsequent siblings)
11 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
Stress file with atomic writes to ensure we excercise codepaths
where we are mixing different FS operations with atomic writes
Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/generic/1229 | 68 ++++++++++++++++++++++++++++++++++++++++++
tests/generic/1229.out | 2 ++
2 files changed, 70 insertions(+)
create mode 100755 tests/generic/1229
create mode 100644 tests/generic/1229.out
diff --git a/tests/generic/1229 b/tests/generic/1229
new file mode 100755
index 00000000..7fa57105
--- /dev/null
+++ b/tests/generic/1229
@@ -0,0 +1,68 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test 1229
+#
+# fuzz fsx with atomic writes
+#
+. ./common/preamble
+. ./common/atomicwrites
+_begin_fstest rw auto quick atomicwrites
+
+_require_odirect
+_require_scratch_write_atomic
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount >> $seqres.full 2>&1
+
+testfile=$SCRATCH_MNT/testfile
+touch $testfile
+
+awu_max=$(_get_atomic_write_unit_max $testfile)
+blksz=$(_get_block_size $SCRATCH_MNT)
+bsize=`$here/src/min_dio_alignment $SCRATCH_MNT $SCRATCH_DEV`
+
+set_fsx_avoid() {
+ local file=$1
+
+ case "$FSTYP" in
+ "ext4")
+ local dev=$(findmnt -n -o SOURCE --target $testfile)
+
+ # fsx insert/collpase range support for ext4+bigalloc is
+ # currently broken, so disable it. Also disable incase we can't
+ # detect bigalloc to be on safer side.
+ if [ -z "$DUMPE2FS_PROG" ]; then
+ echo "dumpe2fs not found, disabling insert/collapse range" >> $seqres.full
+ FSX_AVOID+=" -I -C"
+ return
+ fi
+
+ $DUMPE2FS_PROG -h $dev 2>&1 | grep -q bigalloc && {
+ echo "fsx insert/collapse range not supported with bigalloc. Disabling.." >> $seqres.full
+ FSX_AVOID+=" -I -C"
+ }
+ ;;
+ *)
+ ;;
+ esac
+}
+
+# fsx usage:
+#
+# -N numops: total # operations to do
+# -l flen: the upper bound on file size
+# -o oplen: the upper bound on operation size (64k default)
+# -Z: O_DIRECT ()
+
+set_fsx_avoid
+_run_fsx_on_file $testfile -N 10000 -o $awu_max -A -l 500000 -r $bsize -w $bsize -Z $FSX_AVOID >> $seqres.full
+if [[ "$?" != "0" ]]
+then
+ _fail "fsx returned error: $?"
+fi
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/1229.out b/tests/generic/1229.out
new file mode 100644
index 00000000..737d61c6
--- /dev/null
+++ b/tests/generic/1229.out
@@ -0,0 +1,2 @@
+QA output created by 1229
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 09/12] generic: Add sudden shutdown tests for multi block atomic writes
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (7 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 08/12] generic: Stress fsx with atomic writes enabled Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-09-02 15:49 ` John Garry
2025-08-22 8:02 ` [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc Ojaswin Mujoo
` (2 subsequent siblings)
11 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
This test is intended to ensure that multi blocks atomic writes
maintain atomic guarantees across sudden FS shutdowns.
The way we work is that we lay out a file with random mix of written,
unwritten and hole extents. Then we start performing atomic writes
sequentially on the file while we parallely shutdown the FS. Then we
note the last offset where the atomic write happened just before shut
down and then make sure blocks around it either have completely old
data or completely new data, ie the write was not torn during shutdown.
We repeat the same with completely written, completely unwritten and completely
empty file to ensure these cases are not torn either. Finally, we have a
similar test for append atomic writes
Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/generic/1230 | 397 +++++++++++++++++++++++++++++++++++++++++
tests/generic/1230.out | 2 +
2 files changed, 399 insertions(+)
create mode 100755 tests/generic/1230
create mode 100644 tests/generic/1230.out
diff --git a/tests/generic/1230 b/tests/generic/1230
new file mode 100755
index 00000000..cff5adc0
--- /dev/null
+++ b/tests/generic/1230
@@ -0,0 +1,397 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test No. 1230
+#
+# Test multi block atomic writes with sudden FS shutdowns to ensure
+# the FS is not tearing the write operation
+. ./common/preamble
+. ./common/atomicwrites
+_begin_fstest auto atomicwrites
+
+_require_scratch_write_atomic_multi_fsblock
+_require_atomic_write_test_commands
+_require_scratch_shutdown
+_require_xfs_io_command "truncate"
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount >> $seqres.full
+
+testfile=$SCRATCH_MNT/testfile
+touch $testfile
+
+awu_max=$(_get_atomic_write_unit_max $testfile)
+blksz=$(_get_block_size $SCRATCH_MNT)
+echo "Awu max: $awu_max" >> $seqres.full
+
+num_blocks=$((awu_max / blksz))
+# keep initial value high for dry run. This will be
+# tweaked in dry_run() based on device write speed.
+filesize=$(( 10 * 1024 * 1024 * 1024 ))
+
+_cleanup() {
+ [ -n "$awloop_pid" ] && kill $awloop_pid &> /dev/null
+ wait
+}
+
+atomic_write_loop() {
+ local off=0
+ local size=$awu_max
+ for ((i=0; i<$((filesize / $size )); i++)); do
+ # Due to sudden shutdown this can produce errors so just
+ # redirect them to seqres.full
+ $XFS_IO_PROG -c "open -fsd $testfile" -c "pwrite -S 0x61 -DA -V1 -b $size $off $size" >> /dev/null 2>>$seqres.full
+ echo "Written to offset: $off" >> $tmp.aw
+ off=$((off + $size))
+ done
+}
+
+# This test has the following flow:
+# 1. Start doing sequential atomic writes in bg, upto $filesize
+# 2. Sleep for 0.2s and shutdown the FS
+# 3. kill the atomic write process
+# 4. verify the writes were not torn
+#
+# We ideally want the shutdown to happen while an atomic write is ongoing
+# but this gets tricky since faster devices can actually finish the whole
+# atomic write loop before sleep 0.2s completes, resulting in the shutdown
+# happening after the write loop which is not what we want. A simple solution
+# to this is to increase $filesize so step 1 takes long enough but a big
+# $filesize leads to create_mixed_mappings() taking very long, which is not
+# ideal.
+#
+# Hence, use the dry_run function to figure out the rough device speed and set
+# $filesize accordingly.
+dry_run() {
+ echo >> $seqres.full
+ echo "# Estimating ideal filesize..." >> $seqres.full
+ atomic_write_loop &
+ awloop_pid=$!
+
+ local i=0
+ # Wait for atleast first write to be recorded or 10s
+ while [ ! -f "$tmp.aw" -a $i -le 50 ]; do i=$((i + 1)); sleep 0.2; done
+
+ if [[ $i -gt 50 ]]
+ then
+ _fail "atomic write process took too long to start"
+ fi
+
+ echo >> $seqres.full
+ echo "# Shutting down filesystem while write is running" >> $seqres.full
+ _scratch_shutdown
+
+ kill $awloop_pid 2>/dev/null # the process might have finished already
+ wait $awloop_pid
+ unset $awloop_pid
+
+ bytes_written=$(tail -n 1 $tmp.aw | cut -d" " -f4)
+ echo "# Bytes written in 0.2s: $bytes_written" >> $seqres.full
+
+ filesize=$((bytes_written * 3))
+ echo "# Setting \$filesize=$filesize" >> $seqres.full
+
+ rm $tmp.aw
+ sleep 0.5
+
+ _scratch_cycle_mount
+
+}
+
+create_mixed_mappings() {
+ local file=$1
+ local size_bytes=$2
+
+ echo "# Filling file $file with alternate mappings till size $size_bytes" >> $seqres.full
+ #Fill the file with alternate written and unwritten blocks
+ local off=0
+ local operations=("W" "U")
+
+ for ((i=0; i<$((size_bytes / blksz )); i++)); do
+ index=$(($i % ${#operations[@]}))
+ map="${operations[$index]}"
+
+ case "$map" in
+ "W")
+ $XFS_IO_PROG -fc "pwrite -b $blksz $off $blksz" $file >> /dev/null
+ ;;
+ "U")
+ $XFS_IO_PROG -fc "falloc $off $blksz" $file >> /dev/null
+ ;;
+ esac
+ off=$((off + blksz))
+ done
+
+ sync $file
+}
+
+populate_expected_data() {
+ # create a dummy file with expected old data for different cases
+ create_mixed_mappings $testfile.exp_old_mixed $awu_max
+ expected_data_old_mixed=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_old_mixed)
+
+ $XFS_IO_PROG -fc "falloc 0 $awu_max" $testfile.exp_old_zeroes >> $seqres.full
+ expected_data_old_zeroes=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_old_zeroes)
+
+ $XFS_IO_PROG -fc "pwrite -b $awu_max 0 $awu_max" $testfile.exp_old_mapped >> $seqres.full
+ expected_data_old_mapped=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_old_mapped)
+
+ # create a dummy file with expected new data
+ $XFS_IO_PROG -fc "pwrite -S 0x61 -b $awu_max 0 $awu_max" $testfile.exp_new >> $seqres.full
+ expected_data_new=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_new)
+}
+
+verify_data_blocks() {
+ local verify_start=$1
+ local verify_end=$2
+ local expected_data_old="$3"
+ local expected_data_new="$4"
+
+ echo >> $seqres.full
+ echo "# Checking data integrity from $verify_start to $verify_end" >> $seqres.full
+
+ # After an atomic write, for every chunk we ensure that the underlying
+ # data is either the old data or new data as writes shouldn't get torn.
+ local off=$verify_start
+ while [[ "$off" -lt "$verify_end" ]]
+ do
+ #actual_data=$(xxd -s $off -l $awu_max -p $testfile)
+ actual_data=$(od -An -t x1 -j $off -N $awu_max $testfile)
+ if [[ "$actual_data" != "$expected_data_new" ]] && [[ "$actual_data" != "$expected_data_old" ]]
+ then
+ echo "Checksum match failed at off: $off size: $awu_max"
+ echo "Expected contents: (Either of the 2 below):"
+ echo
+ echo "Expected old: "
+ echo "$expected_data_old"
+ echo
+ echo "Expected new: "
+ echo "$expected_data_new"
+ echo
+ echo "Actual contents: "
+ echo "$actual_data"
+
+ _fail
+ fi
+ echo -n "Check at offset $off suceeded! " >> $seqres.full
+ if [[ "$actual_data" == "$expected_data_new" ]]
+ then
+ echo "matched new" >> $seqres.full
+ elif [[ "$actual_data" == "$expected_data_old" ]]
+ then
+ echo "matched old" >> $seqres.full
+ fi
+ off=$(( off + awu_max ))
+ done
+}
+
+# test data integrity for file by shutting down in between atomic writes
+test_data_integrity() {
+ echo >> $seqres.full
+ echo "# Writing atomically to file in background" >> $seqres.full
+ atomic_write_loop &
+ awloop_pid=$!
+
+ local i=0
+ # Wait for atleast first write to be recorded or 10s
+ while [ ! -f "$tmp.aw" -a $i -le 50 ]; do i=$((i + 1)); sleep 0.2; done
+
+ if [[ $i -gt 50 ]]
+ then
+ _fail "atomic write process took too long to start"
+ fi
+
+ echo >> $seqres.full
+ echo "# Shutting down filesystem while write is running" >> $seqres.full
+ _scratch_shutdown
+
+ kill $awloop_pid 2>/dev/null # the process might have finished already
+ wait $awloop_pid
+ unset $awloop_pid
+
+ last_offset=$(tail -n 1 $tmp.aw | cut -d" " -f4)
+ if [[ -z $last_offset ]]
+ then
+ last_offset=0
+ fi
+
+ echo >> $seqres.full
+ echo "# Last offset of atomic write: $last_offset" >> $seqres.full
+
+ rm $tmp.aw
+ sleep 0.5
+
+ _scratch_cycle_mount
+
+ # we want to verify all blocks around which the shutdown happended
+ verify_start=$(( last_offset - (awu_max * 5)))
+ if [[ $verify_start < 0 ]]
+ then
+ verify_start=0
+ fi
+
+ verify_end=$(( last_offset + (awu_max * 5)))
+ if [[ "$verify_end" -gt "$filesize" ]]
+ then
+ verify_end=$filesize
+ fi
+}
+
+# test data integrity for file wiht written and unwritten mappings
+test_data_integrity_mixed() {
+ $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
+
+ echo >> $seqres.full
+ echo "# Creating testfile with mixed mappings" >> $seqres.full
+ create_mixed_mappings $testfile $filesize
+
+ test_data_integrity
+
+ verify_data_blocks $verify_start $verify_end "$expected_data_old_mixed" "$expected_data_new"
+}
+
+# test data integrity for file with completely written mappings
+test_data_integrity_writ() {
+ $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
+
+ echo >> $seqres.full
+ echo "# Creating testfile with fully written mapping" >> $seqres.full
+ $XFS_IO_PROG -c "pwrite -b $filesize 0 $filesize" $testfile >> $seqres.full
+ sync $testfile
+
+ test_data_integrity
+
+ verify_data_blocks $verify_start $verify_end "$expected_data_old_mapped" "$expected_data_new"
+}
+
+# test data integrity for file with completely unwritten mappings
+test_data_integrity_unwrit() {
+ $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
+
+ echo >> $seqres.full
+ echo "# Creating testfile with fully unwritten mappings" >> $seqres.full
+ $XFS_IO_PROG -c "falloc 0 $filesize" $testfile >> $seqres.full
+ sync $testfile
+
+ test_data_integrity
+
+ verify_data_blocks $verify_start $verify_end "$expected_data_old_zeroes" "$expected_data_new"
+}
+
+# test data integrity for file with no mappings
+test_data_integrity_hole() {
+ $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
+
+ echo >> $seqres.full
+ echo "# Creating testfile with no mappings" >> $seqres.full
+ $XFS_IO_PROG -c "truncate $filesize" $testfile >> $seqres.full
+ sync $testfile
+
+ test_data_integrity
+
+ verify_data_blocks $verify_start $verify_end "$expected_data_old_zeroes" "$expected_data_new"
+}
+
+test_filesize_integrity() {
+ $XFS_IO_PROG -c "truncate 0" $testfile >> $seqres.full
+
+ echo >> $seqres.full
+ echo "# Performing extending atomic writes over file in background" >> $seqres.full
+ atomic_write_loop &
+ awloop_pid=$!
+
+ local i=0
+ # Wait for atleast first write to be recorded or 10s
+ while [ ! -f "$tmp.aw" -a $i -le 50 ]; do i=$((i + 1)); sleep 0.2; done
+
+ if [[ $i -gt 50 ]]
+ then
+ _fail "atomic write process took too long to start"
+ fi
+
+ echo >> $seqres.full
+ echo "# Shutting down filesystem while write is running" >> $seqres.full
+ _scratch_shutdown
+
+ kill $awloop_pid 2>/dev/null # the process might have finished already
+ wait $awloop_pid
+ unset $awloop_pid
+
+ local last_offset=$(tail -n 1 $tmp.aw | cut -d" " -f4)
+ if [[ -z $last_offset ]]
+ then
+ last_offset=0
+ fi
+
+ echo >> $seqres.full
+ echo "# Last offset of atomic write: $last_offset" >> $seqres.full
+ rm $tmp.aw
+ sleep 0.5
+
+ _scratch_cycle_mount
+ local filesize=$(_get_filesize $testfile)
+ echo >> $seqres.full
+ echo "# Filesize after shutdown: $filesize" >> $seqres.full
+
+ # To confirm that the write went atomically, we check:
+ # 1. The last block should be a multiple of awu_max
+ # 2. The last block should be the completely new data
+
+ if (( $filesize % $awu_max ))
+ then
+ echo "Filesize after shutdown ($filesize) not a multiple of atomic write unit ($awu_max)"
+ fi
+
+ verify_start=$(( filesize - (awu_max * 5)))
+ if [[ $verify_start < 0 ]]
+ then
+ verify_start=0
+ fi
+
+ local verify_end=$filesize
+
+ # Here the blocks should always match new data hence, for simplicity of
+ # code, just corrupt the $expected_data_old buffer so it never matches
+ local expected_data_old="POISON"
+ verify_data_blocks $verify_start $verify_end "$expected_data_old" "$expected_data_new"
+}
+
+$XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
+
+dry_run
+
+echo >> $seqres.full
+echo "# Populating expected data buffers" >> $seqres.full
+populate_expected_data
+
+# Loop 20 times to shake out any races due to shutdown
+for ((iter=0; iter<20; iter++))
+do
+ echo >> $seqres.full
+ echo "------ Iteration $iter ------" >> $seqres.full
+
+ echo >> $seqres.full
+ echo "# Starting data integrity test for atomic writes over mixed mapping" >> $seqres.full
+ test_data_integrity_mixed
+
+ echo >> $seqres.full
+ echo "# Starting data integrity test for atomic writes over fully written mapping" >> $seqres.full
+ test_data_integrity_writ
+
+ echo >> $seqres.full
+ echo "# Starting data integrity test for atomic writes over fully unwritten mapping" >> $seqres.full
+ test_data_integrity_unwrit
+
+ echo >> $seqres.full
+ echo "# Starting data integrity test for atomic writes over holes" >> $seqres.full
+ test_data_integrity_hole
+
+ echo >> $seqres.full
+ echo "# Starting filesize integrity test for atomic writes" >> $seqres.full
+ test_filesize_integrity
+done
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/1230.out b/tests/generic/1230.out
new file mode 100644
index 00000000..d01f54ea
--- /dev/null
+++ b/tests/generic/1230.out
@@ -0,0 +1,2 @@
+QA output created by 1230
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (8 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 09/12] generic: Add sudden shutdown tests for multi block atomic writes Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-08-28 15:09 ` Darrick J. Wong
2025-09-02 15:52 ` John Garry
2025-08-22 8:02 ` [PATCH v5 11/12] ext4: Test atomic writes allocation and write " Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 12/12] ext4: Atomic write test for extent split across leaf nodes Ojaswin Mujoo
11 siblings, 2 replies; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
This test does a lot of parallel RWF_ATOMIC IO on a preallocated file to
stress the write and end-io unwritten conversion code paths. We brute
force this for different blocksize and clustersizes and after each
iteration we ensure the data was not torn or corrupted using fio crc
verification.
Note that in this test we use overlapping atomic writes of same io size.
Although serializing racing writes is not guaranteed for RWF_ATOMIC,
NVMe and SCSI provide this guarantee as an inseparable feature to
power-fail atomicity. Keeping the iosize as same also ensures that ext4
doesn't tear the write due to racing ioend unwritten conversion.
The value of this test is that we make sure the RWF_ATOMIC is handled
correctly by ext4 as well as test that the block layer doesn't split or
only generate multiple bios for an atomic write.
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/ext4/061 | 155 +++++++++++++++++++++++++++++++++++++++++++++
tests/ext4/061.out | 2 +
2 files changed, 157 insertions(+)
create mode 100755 tests/ext4/061
create mode 100644 tests/ext4/061.out
diff --git a/tests/ext4/061 b/tests/ext4/061
new file mode 100755
index 00000000..0ccf9f69
--- /dev/null
+++ b/tests/ext4/061
@@ -0,0 +1,155 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test 061
+#
+# This test does a lot of parallel RWF_ATOMIC IO on a preallocated file to
+# stress the write and end-io unwritten conversion code paths. We brute force
+# this for all possible blocksize and clustersizes and after each iteration we
+# ensure the data was not torn or corrupted using fio crc verification.
+#
+# Note that in this test we use overlapping atomic writes of same io size.
+# Although serializing racing writes is not guaranteed for RWF_ATOMIC, NVMe and
+# SCSI provide this guarantee as an inseparable feature to power-fail
+# atomicity. Keeping the iosize as same also ensures that ext4 doesn't tear the
+# write due to racing ioend unwritten conversion.
+#
+# The value of this test is that we make sure the RWF_ATOMIC is handled
+# correctly by ext4 as well as test that the block layer doesn't split or only
+# generate multiple bios for an atomic write.
+
+. ./common/preamble
+. ./common/atomicwrites
+
+_begin_fstest auto rw stress atomicwrites
+
+_require_scratch_write_atomic
+_require_aiodio
+_require_fio_version "3.38+"
+
+FIO_LOAD=$(($(nproc) * 2 * LOAD_FACTOR))
+SIZE=$((100*1024*1024))
+
+# Calculate fsblocksize as per bdev atomic write units.
+bdev_awu_min=$(_get_atomic_write_unit_min $SCRATCH_DEV)
+bdev_awu_max=$(_get_atomic_write_unit_max $SCRATCH_DEV)
+bs=$(_max 4096 "$bdev_awu_min")
+
+function create_fio_configs()
+{
+ local bsize=$1
+ create_fio_aw_config $bsize
+ create_fio_verify_config $bsize
+}
+
+function create_fio_verify_config()
+{
+ local bsize=$1
+cat >$fio_verify_config <<EOF
+ [aio-dio-aw-verify]
+ direct=1
+ ioengine=libaio
+ rw=read
+ bs=$bsize
+ fallocate=native
+ filename=$SCRATCH_MNT/test-file
+ size=$SIZE
+ iodepth=$FIO_LOAD
+ numjobs=$FIO_LOAD
+ atomic=1
+ group_reporting=1
+
+ verify_only=1
+ verify_state_save=0
+ verify=crc32c
+ verify_fatal=1
+ verify_write_sequence=0
+EOF
+}
+
+function create_fio_aw_config()
+{
+ local bsize=$1
+cat >$fio_aw_config <<EOF
+ [aio-dio-aw]
+ direct=1
+ ioengine=libaio
+ rw=randwrite
+ bs=$bsize
+ fallocate=native
+ filename=$SCRATCH_MNT/test-file
+ size=$SIZE
+ iodepth=$FIO_LOAD
+ numjobs=$FIO_LOAD
+ group_reporting=1
+ atomic=1
+
+ verify_state_save=0
+ verify=crc32c
+ do_verify=0
+
+EOF
+}
+
+run_test_one() {
+ local bs=$1
+ local cs=$2
+ local iosize=$3
+
+ MKFS_OPTIONS="-O bigalloc -b $bs -C $cs"
+ _scratch_mkfs_ext4 >> $seqres.full 2>&1 || continue
+ if _try_scratch_mount >> $seqres.full 2>&1; then
+ echo "== Testing: bs=$bs cs=$cs iosize=$iosize ==" >> $seqres.full
+
+ touch $SCRATCH_MNT/f1
+ create_fio_configs $iosize
+
+ cat $fio_aw_config >> $seqres.full
+ echo >> $seqres.full
+ cat $fio_verify_config >> $seqres.full
+
+ $FIO_PROG $fio_aw_config >> $seqres.full
+ ret1=$?
+
+ $FIO_PROG $fio_verify_config >> $seqres.full
+ ret2=$?
+
+ _scratch_unmount
+
+ [[ $ret1 -eq 0 && $ret2 -eq 0 ]] || _fail "fio with atomic write failed"
+ fi
+}
+
+run_test() {
+ local bs=$1
+
+ # cluster sizes above 16 x blocksize are experimental so avoid them
+ # Also, cap cluster size at 128kb to keep it reasonable for large
+ # blocks size
+ max_cs=$(_min $((16 * bs)) "$bdev_awu_max" $((128 * 1024)))
+
+ # Fuzz for combinations of blocksize, clustersize and
+ # iosize that cover most of the cases
+ run_test_one $bs $bs $bs
+ run_test_one $bs $max_cs $bs
+ run_test_one $bs $max_cs $max_cs
+ run_test_one $bs $max_cs $(_max "$((max_cs/2))" $bs)
+}
+
+# Let's create a sample fio config to check whether fio supports all options.
+fio_aw_config=$tmp.aw.fio
+fio_verify_config=$tmp.verify.fio
+fio_out=$tmp.fio.out
+
+create_fio_configs $bs
+_require_fio $fio_aw_config
+
+for ((bs=$bs; bs <= $(_get_page_size); bs = $bs << 1)); do
+ run_test $bs
+done
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/ext4/061.out b/tests/ext4/061.out
new file mode 100644
index 00000000..273be9e0
--- /dev/null
+++ b/tests/ext4/061.out
@@ -0,0 +1,2 @@
+QA output created by 061
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 11/12] ext4: Test atomic writes allocation and write codepaths with bigalloc
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (9 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
2025-09-02 15:54 ` John Garry
2025-08-22 8:02 ` [PATCH v5 12/12] ext4: Atomic write test for extent split across leaf nodes Ojaswin Mujoo
11 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
This test does a parallel RWF_ATOMIC IO on a multiple truncated files in
a small FS. The idea is to stress ext4 allocator to ensure we are able
to handle low space scenarios correctly with atomic writes. We brute
force this for different blocksize and clustersizes and after each
iteration we ensure the data was not torn or corrupted using fio crc
verification.
Note that in this test we use overlapping atomic writes of same io size.
Although serializing racing writes is not guaranteed for RWF_ATOMIC,
NVMe and SCSI provide this guarantee as an inseparable feature to
power-fail atomicity. Keeping the iosize as same also ensures that ext4
doesn't tear the write due to racing ioend unwritten conversion.
The value of this test is that we make sure the RWF_ATOMIC is handled
correctly by ext4 as well as test that the block layer doesn't split or
only generate multiple bios for an atomic write.
Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/ext4/062 | 203 +++++++++++++++++++++++++++++++++++++++++++++
tests/ext4/062.out | 2 +
2 files changed, 205 insertions(+)
create mode 100755 tests/ext4/062
create mode 100644 tests/ext4/062.out
diff --git a/tests/ext4/062 b/tests/ext4/062
new file mode 100755
index 00000000..d48f69d3
--- /dev/null
+++ b/tests/ext4/062
@@ -0,0 +1,203 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test 062
+#
+# This test does a parallel RWF_ATOMIC IO on a multiple truncated files in a
+# small FS. The idea is to stress ext4 allocator to ensure we are able to
+# handle low space scenarios correctly with atomic writes.. We brute force this
+# for all possible blocksize and clustersizes and after each iteration we
+# ensure the data was not torn or corrupted using fio crc verification.
+#
+# Note that in this test we use overlapping atomic writes of same io size.
+# Although serializing racing writes is not guaranteed for RWF_ATOMIC, NVMe and
+# SCSI provide this guarantee as an inseparable feature to power-fail
+# atomicity. Keeping the iosize as same also ensures that ext4 doesn't tear the
+# write due to racing ioend unwritten conversion.
+#
+# The value of this test is that we make sure the RWF_ATOMIC is handled
+# correctly by ext4 as well as test that the block layer doesn't split or only
+# generate multiple bios for an atomic write.
+#
+
+. ./common/preamble
+. ./common/atomicwrites
+
+_begin_fstest auto rw stress atomicwrites
+
+_require_scratch_write_atomic
+_require_aiodio
+_require_fio_version "3.38+"
+
+FSSIZE=$((360*1024*1024))
+FIO_LOAD=$(($(nproc) * LOAD_FACTOR))
+
+# Calculate bs as per bdev atomic write units.
+bdev_awu_min=$(_get_atomic_write_unit_min $SCRATCH_DEV)
+bdev_awu_max=$(_get_atomic_write_unit_max $SCRATCH_DEV)
+bs=$(_max 4096 "$bdev_awu_min")
+
+function create_fio_configs()
+{
+ local bsize=$1
+ create_fio_aw_config $bsize
+ create_fio_verify_config $bsize
+}
+
+function create_fio_verify_config()
+{
+ local bsize=$1
+cat >$fio_verify_config <<EOF
+ [global]
+ direct=1
+ ioengine=libaio
+ rw=read
+ bs=$bsize
+ fallocate=truncate
+ size=$((FSSIZE / 12))
+ iodepth=$FIO_LOAD
+ numjobs=$FIO_LOAD
+ group_reporting=1
+ atomic=1
+
+ verify_only=1
+ verify_state_save=0
+ verify=crc32c
+ verify_fatal=1
+ verify_write_sequence=0
+
+ [verify-job1]
+ filename=$SCRATCH_MNT/testfile-job1
+
+ [verify-job2]
+ filename=$SCRATCH_MNT/testfile-job2
+
+ [verify-job3]
+ filename=$SCRATCH_MNT/testfile-job3
+
+ [verify-job4]
+ filename=$SCRATCH_MNT/testfile-job4
+
+ [verify-job5]
+ filename=$SCRATCH_MNT/testfile-job5
+
+ [verify-job6]
+ filename=$SCRATCH_MNT/testfile-job6
+
+ [verify-job7]
+ filename=$SCRATCH_MNT/testfile-job7
+
+ [verify-job8]
+ filename=$SCRATCH_MNT/testfile-job8
+
+EOF
+}
+
+function create_fio_aw_config()
+{
+ local bsize=$1
+cat >$fio_aw_config <<EOF
+ [global]
+ direct=1
+ ioengine=libaio
+ rw=randwrite
+ bs=$bsize
+ fallocate=truncate
+ size=$((FSSIZE / 12))
+ iodepth=$FIO_LOAD
+ numjobs=$FIO_LOAD
+ group_reporting=1
+ atomic=1
+
+ verify_state_save=0
+ verify=crc32c
+ do_verify=0
+
+ [write-job1]
+ filename=$SCRATCH_MNT/testfile-job1
+
+ [write-job2]
+ filename=$SCRATCH_MNT/testfile-job2
+
+ [write-job3]
+ filename=$SCRATCH_MNT/testfile-job3
+
+ [write-job4]
+ filename=$SCRATCH_MNT/testfile-job4
+
+ [write-job5]
+ filename=$SCRATCH_MNT/testfile-job5
+
+ [write-job6]
+ filename=$SCRATCH_MNT/testfile-job6
+
+ [write-job7]
+ filename=$SCRATCH_MNT/testfile-job7
+
+ [write-job8]
+ filename=$SCRATCH_MNT/testfile-job8
+
+EOF
+}
+
+run_test_one() {
+ local bs=$1
+ local cs=$2
+ local iosize=$3
+
+ MKFS_OPTIONS="-O bigalloc -b $bs -C $cs"
+ _scratch_mkfs_sized "$FSSIZE" >> $seqres.full 2>&1 || continue
+ if _try_scratch_mount >> $seqres.full 2>&1; then
+ echo "Testing: bs=$bs cs=$cs iosize=$iosize" >> $seqres.full
+
+ touch $SCRATCH_MNT/f1
+ create_fio_configs $iosize
+
+ cat $fio_aw_config >> $seqres.full
+ cat $fio_verify_config >> $seqres.full
+
+ $FIO_PROG $fio_aw_config >> $seqres.full
+ ret1=$?
+
+ $FIO_PROG $fio_verify_config >> $seqres.full
+ ret2=$?
+
+ _scratch_unmount
+
+ [[ $ret1 -eq 0 && $ret2 -eq 0 ]] || _fail "fio with atomic write failed"
+ fi
+}
+
+run_test() {
+ local bs=$1
+
+ # cluster sizes above 16 x blocksize are experimental so avoid them
+ # Also, cap cluster size at 128kb to keep it reasonable for large
+ # blocks size
+ max_cs=$(_min $((16 * bs)) "$bdev_awu_max" $((128 * 1024)))
+
+ # Fuzz for combinations of blocksize, clustersize and
+ # iosize that cover most of the cases
+ run_test_one $bs $bs $bs
+ run_test_one $bs $max_cs $bs
+ run_test_one $bs $max_cs $max_cs
+ run_test_one $bs $max_cs $(_max "$((max_cs/2))" $bs)
+}
+
+# Let's create a sample fio config to check whether fio supports all options.
+fio_aw_config=$tmp.aw.fio
+fio_verify_config=$tmp.verify.fio
+fio_out=$tmp.fio.out
+
+create_fio_configs $bs
+_require_fio $fio_aw_config
+
+for ((bs=$bs; bs <= $(_get_page_size); bs = $bs << 1)); do
+ run_test $bs $cs $iosize
+done
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/ext4/062.out b/tests/ext4/062.out
new file mode 100644
index 00000000..a1578f48
--- /dev/null
+++ b/tests/ext4/062.out
@@ -0,0 +1,2 @@
+QA output created by 062
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v5 12/12] ext4: Atomic write test for extent split across leaf nodes
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
` (10 preceding siblings ...)
2025-08-22 8:02 ` [PATCH v5 11/12] ext4: Test atomic writes allocation and write " Ojaswin Mujoo
@ 2025-08-22 8:02 ` Ojaswin Mujoo
11 siblings, 0 replies; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-22 8:02 UTC (permalink / raw)
To: Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
In ext4, even if an allocated range is physically and logically
contiguous, it can still be split into 2 extents. This is because ext4
does not merge extents across leaf nodes. This is an issue for atomic
writes since even for a continuous extent the map block could (in rare
cases) return a shorter map, hence tearning the write. This test creates
such a file and ensures that the atomic write handles this case
correctly
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
tests/ext4/063 | 129 +++++++++++++++++++++++++++++++++++++++++++++
tests/ext4/063.out | 2 +
2 files changed, 131 insertions(+)
create mode 100755 tests/ext4/063
create mode 100644 tests/ext4/063.out
diff --git a/tests/ext4/063 b/tests/ext4/063
new file mode 100755
index 00000000..19fb611b
--- /dev/null
+++ b/tests/ext4/063
@@ -0,0 +1,129 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# In ext4, even if an allocated range is physically and logically contiguous,
+# it can still be split into 2 or more extents. This is because ext4 does not
+# merge extents across leaf nodes. This is an issue for atomic writes since
+# even for a continuous extent the map block could (in rare cases) return a
+# shorter map, hence tearing the write. This test creates such a file and
+# ensures that the atomic write handles this case correctly
+#
+. ./common/preamble
+. ./common/atomicwrites
+_begin_fstest auto atomicwrites
+
+_require_scratch_write_atomic_multi_fsblock
+_require_atomic_write_test_commands
+_require_command "$DEBUGFS_PROG" debugfs
+
+prep() {
+ local bs=`_get_block_size $SCRATCH_MNT`
+ local ex_hdr_bytes=12
+ local ex_entry_bytes=12
+ local entries_per_blk=$(( (bs - ex_hdr_bytes) / ex_entry_bytes ))
+
+ # fill the extent tree leaf with bs len extents at alternate offsets.
+ # The tree should look as follows
+ #
+ # +---------+---------+
+ # | index 1 | index 2 |
+ # +-----+---+-----+---+
+ # +------+ +-----------+
+ # | |
+ # +-------+-------+---+---------+ +-----+----+
+ # | ex 1 | ex 2 | | ex n | | ex n+1 |
+ # | off:0 | off:2 |...| off:678 | | off:680 |
+ # | len:1 | len:1 | | len:1 | | len:1 |
+ # +-------+-------+---+---------+ +----------+
+ #
+ for i in $(seq 0 $entries_per_blk)
+ do
+ $XFS_IO_PROG -fc "pwrite -b $bs $((i * 2 * bs)) $bs" $testfile > /dev/null
+ done
+ sync $testfile
+
+ echo >> $seqres.full
+ echo "Create file with extents spanning 2 leaves. Extents:">> $seqres.full
+ echo "...">> $seqres.full
+ $DEBUGFS_PROG -R "ex `basename $testfile`" $SCRATCH_DEV |& tail >> $seqres.full
+
+ # Now try to insert a new extent ex(new) between ex(n) and ex(n+1).
+ # Since this is a new FS the allocator would find continuous blocks
+ # such that ex(n) ex(new) ex(n+1) are physically(and logically)
+ # contiguous. However, since we don't merge extents across leaf we will
+ # end up with a tree as:
+ #
+ # +---------+---------+
+ # | index 1 | index 2 |
+ # +-----+---+-----+---+
+ # +------+ +------------+
+ # | |
+ # +-------+-------+---+---------+ +------+-----------+
+ # | ex 1 | ex 2 | | ex n | | ex n+1 (merged) |
+ # | off:0 | off:2 |...| off:678 | | off:679 |
+ # | len:1 | len:1 | | len:1 | | len:2 |
+ # +-------+-------+---+---------+ +------------------+
+ #
+ echo >> $seqres.full
+ torn_ex_offset=$((((entries_per_blk * 2) - 1) * bs))
+ $XFS_IO_PROG -c "pwrite $torn_ex_offset $bs" $testfile >> /dev/null
+ sync $testfile
+
+ echo >> $seqres.full
+ echo "Perform 1 block write at $torn_ex_offset to create torn extent. Extents:">> $seqres.full
+ echo "...">> $seqres.full
+ $DEBUGFS_PROG -R "ex `basename $testfile`" $SCRATCH_DEV |& tail >> $seqres.full
+
+ _scratch_cycle_mount
+}
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+testfile=$SCRATCH_MNT/testfile
+touch $testfile
+awu_max=$(_get_atomic_write_unit_max $testfile)
+
+echo >> $seqres.full
+echo "# Prepping the file" >> $seqres.full
+prep
+
+torn_aw_offset=$((torn_ex_offset - (torn_ex_offset % awu_max)))
+
+echo >> $seqres.full
+echo "# Performing atomic IO on the torn extent range. Command: " >> $seqres.full
+echo $XFS_IO_PROG -c "open -fsd $testfile" -c "pwrite -S 0x61 -DA -V1 -b $awu_max $torn_aw_offset $awu_max" >> $seqres.full
+$XFS_IO_PROG -c "open -fsd $testfile" -c "pwrite -S 0x61 -DA -V1 -b $awu_max $torn_aw_offset $awu_max" >> $seqres.full
+
+echo >> $seqres.full
+echo "Extent state after atomic write:">> $seqres.full
+echo "...">> $seqres.full
+$DEBUGFS_PROG -R "ex `basename $testfile`" $SCRATCH_DEV |& tail >> $seqres.full
+
+echo >> $seqres.full
+echo "# Checking data integrity" >> $seqres.full
+
+# create a dummy file with expected data
+$XFS_IO_PROG -fc "pwrite -S 0x61 -b $awu_max 0 $awu_max" $testfile.exp >> /dev/null
+expected_data=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp)
+
+# We ensure that the data after atomic writes should match the expected data
+actual_data=$(od -An -t x1 -j $torn_aw_offset -N $awu_max $testfile)
+if [[ "$actual_data" != "$expected_data" ]]
+then
+ echo "Checksum match failed at off: $torn_aw_offset size: $awu_max"
+ echo
+ echo "Expected: "
+ echo "$expected_data"
+ echo
+ echo "Actual contents: "
+ echo "$actual_data"
+
+ _fail
+fi
+
+echo -n "Data verification at offset $torn_aw_offset suceeded!" >> $seqres.full
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/ext4/063.out b/tests/ext4/063.out
new file mode 100644
index 00000000..de35fc52
--- /dev/null
+++ b/tests/ext4/063.out
@@ -0,0 +1,2 @@
+QA output created by 063
+Silence is golden
--
2.49.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-22 8:02 ` [PATCH v5 02/12] common/rc: Add _require_fio_version helper Ojaswin Mujoo
@ 2025-08-25 16:08 ` Zorro Lang
2025-08-27 15:16 ` Ojaswin Mujoo
2025-09-02 14:50 ` John Garry
1 sibling, 1 reply; 31+ messages in thread
From: Zorro Lang @ 2025-08-25 16:08 UTC (permalink / raw)
To: Ojaswin Mujoo
Cc: fstests, Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
On Fri, Aug 22, 2025 at 01:32:01PM +0530, Ojaswin Mujoo wrote:
> The main motivation of adding this function on top of _require_fio is
> that there has been a case in fio where atomic= option was added but
> later it was changed to noop since kernel didn't yet have support for
> atomic writes. It was then again utilized to do atomic writes in a later
> version, once kernel got the support. Due to this there is a point in
> fio where _require_fio w/ atomic=1 will succeed even though it would
> not be doing atomic writes.
>
> Hence, add an explicit helper to ensure tests to require specific
> versions of fio to work past such issues.
Actually I'm wondering if fstests really needs to care about this. This's
just a temporary issue of fio, not kernel or any fs usespace program. Do
we need to add a seperated helper only for a temporary fio issue? If fio
doesn't break fstests running, let it run. Just the testers install proper
fio (maybe latest) they need. What do you and others think?
Thanks,
Zorro
>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> ---
> common/rc | 32 ++++++++++++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/common/rc b/common/rc
> index 35a1c835..f45b9a38 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -5997,6 +5997,38 @@ _max() {
> echo $ret
> }
>
> +# Check the required fio version. Examples:
> +# _require_fio_version 3.38 (matches 3.38 only)
> +# _require_fio_version 3.38+ (matches 3.38 and above)
> +# _require_fio_version 3.38- (matches 3.38 and below)
> +_require_fio_version() {
> + local req_ver="$1"
> + local fio_ver
> +
> + _require_fio
> + _require_math
> +
> + fio_ver=$(fio -v | cut -d"-" -f2)
> +
> + case "$req_ver" in
> + *+)
> + req_ver=${req_ver%+}
> + test $(_math "$fio_ver >= $req_ver") -eq 1 || \
> + _notrun "need fio >= $req_ver (found $fio_ver)"
> + ;;
> + *-)
> + req_ver=${req_ver%-}
> + test $(_math "$fio_ver <= $req_ver") -eq 1 || \
> + _notrun "need fio <= $req_ver (found $fio_ver)"
> + ;;
> + *)
> + req_ver=${req_ver%-}
> + test $(_math "$fio_ver == $req_ver") -eq 1 || \
> + _notrun "need fio = $req_ver (found $fio_ver)"
> + ;;
> + esac
> +}
> +
> ################################################################################
> # make sure this script returns success
> /bin/true
> --
> 2.49.0
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-25 16:08 ` Zorro Lang
@ 2025-08-27 15:16 ` Ojaswin Mujoo
2025-08-28 15:09 ` Darrick J. Wong
0 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-27 15:16 UTC (permalink / raw)
To: Zorro Lang
Cc: fstests, Ritesh Harjani, djwong, john.g.garry, tytso, linux-xfs,
linux-kernel, linux-ext4
On Tue, Aug 26, 2025 at 12:08:01AM +0800, Zorro Lang wrote:
> On Fri, Aug 22, 2025 at 01:32:01PM +0530, Ojaswin Mujoo wrote:
> > The main motivation of adding this function on top of _require_fio is
> > that there has been a case in fio where atomic= option was added but
> > later it was changed to noop since kernel didn't yet have support for
> > atomic writes. It was then again utilized to do atomic writes in a later
> > version, once kernel got the support. Due to this there is a point in
> > fio where _require_fio w/ atomic=1 will succeed even though it would
> > not be doing atomic writes.
> >
> > Hence, add an explicit helper to ensure tests to require specific
> > versions of fio to work past such issues.
>
> Actually I'm wondering if fstests really needs to care about this. This's
> just a temporary issue of fio, not kernel or any fs usespace program. Do
> we need to add a seperated helper only for a temporary fio issue? If fio
> doesn't break fstests running, let it run. Just the testers install proper
> fio (maybe latest) they need. What do you and others think?
>
> Thanks,
> Zorro
Hey Zorro,
Sure I'm okay with not keeping the helper and letting the user make sure
the fio version is correct.
@John, does that sound okay?
Regards,
ojaswin
>
> >
> > Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> > ---
> > common/rc | 32 ++++++++++++++++++++++++++++++++
> > 1 file changed, 32 insertions(+)
> >
> > diff --git a/common/rc b/common/rc
> > index 35a1c835..f45b9a38 100644
> > --- a/common/rc
> > +++ b/common/rc
> > @@ -5997,6 +5997,38 @@ _max() {
> > echo $ret
> > }
> >
> > +# Check the required fio version. Examples:
> > +# _require_fio_version 3.38 (matches 3.38 only)
> > +# _require_fio_version 3.38+ (matches 3.38 and above)
> > +# _require_fio_version 3.38- (matches 3.38 and below)
> > +_require_fio_version() {
> > + local req_ver="$1"
> > + local fio_ver
> > +
> > + _require_fio
> > + _require_math
> > +
> > + fio_ver=$(fio -v | cut -d"-" -f2)
> > +
> > + case "$req_ver" in
> > + *+)
> > + req_ver=${req_ver%+}
> > + test $(_math "$fio_ver >= $req_ver") -eq 1 || \
> > + _notrun "need fio >= $req_ver (found $fio_ver)"
> > + ;;
> > + *-)
> > + req_ver=${req_ver%-}
> > + test $(_math "$fio_ver <= $req_ver") -eq 1 || \
> > + _notrun "need fio <= $req_ver (found $fio_ver)"
> > + ;;
> > + *)
> > + req_ver=${req_ver%-}
> > + test $(_math "$fio_ver == $req_ver") -eq 1 || \
> > + _notrun "need fio = $req_ver (found $fio_ver)"
> > + ;;
> > + esac
> > +}
> > +
> > ################################################################################
> > # make sure this script returns success
> > /bin/true
> > --
> > 2.49.0
> >
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-27 15:16 ` Ojaswin Mujoo
@ 2025-08-28 15:09 ` Darrick J. Wong
2025-08-29 16:59 ` Ojaswin Mujoo
0 siblings, 1 reply; 31+ messages in thread
From: Darrick J. Wong @ 2025-08-28 15:09 UTC (permalink / raw)
To: Ojaswin Mujoo
Cc: Zorro Lang, fstests, Ritesh Harjani, john.g.garry, tytso,
linux-xfs, linux-kernel, linux-ext4
On Wed, Aug 27, 2025 at 08:46:34PM +0530, Ojaswin Mujoo wrote:
> On Tue, Aug 26, 2025 at 12:08:01AM +0800, Zorro Lang wrote:
> > On Fri, Aug 22, 2025 at 01:32:01PM +0530, Ojaswin Mujoo wrote:
> > > The main motivation of adding this function on top of _require_fio is
> > > that there has been a case in fio where atomic= option was added but
> > > later it was changed to noop since kernel didn't yet have support for
> > > atomic writes. It was then again utilized to do atomic writes in a later
> > > version, once kernel got the support. Due to this there is a point in
> > > fio where _require_fio w/ atomic=1 will succeed even though it would
> > > not be doing atomic writes.
> > >
> > > Hence, add an explicit helper to ensure tests to require specific
> > > versions of fio to work past such issues.
> >
> > Actually I'm wondering if fstests really needs to care about this. This's
> > just a temporary issue of fio, not kernel or any fs usespace program. Do
> > we need to add a seperated helper only for a temporary fio issue? If fio
> > doesn't break fstests running, let it run. Just the testers install proper
> > fio (maybe latest) they need. What do you and others think?
Are there obvious failures if you try to run these new atomic write
tests on a system with the weird versions of fio that have the no-op
atomic= functionality? I'm concerned that some QA person is going to do
that unwittingly and report that everything is ok when in reality they
didn't actually test anything.
--D
> > Thanks,
> > Zorro
>
> Hey Zorro,
>
> Sure I'm okay with not keeping the helper and letting the user make sure
> the fio version is correct.
>
> @John, does that sound okay?
>
> Regards,
> ojaswin
> >
> > >
> > > Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> > > ---
> > > common/rc | 32 ++++++++++++++++++++++++++++++++
> > > 1 file changed, 32 insertions(+)
> > >
> > > diff --git a/common/rc b/common/rc
> > > index 35a1c835..f45b9a38 100644
> > > --- a/common/rc
> > > +++ b/common/rc
> > > @@ -5997,6 +5997,38 @@ _max() {
> > > echo $ret
> > > }
> > >
> > > +# Check the required fio version. Examples:
> > > +# _require_fio_version 3.38 (matches 3.38 only)
> > > +# _require_fio_version 3.38+ (matches 3.38 and above)
> > > +# _require_fio_version 3.38- (matches 3.38 and below)
> > > +_require_fio_version() {
> > > + local req_ver="$1"
> > > + local fio_ver
> > > +
> > > + _require_fio
> > > + _require_math
> > > +
> > > + fio_ver=$(fio -v | cut -d"-" -f2)
> > > +
> > > + case "$req_ver" in
> > > + *+)
> > > + req_ver=${req_ver%+}
> > > + test $(_math "$fio_ver >= $req_ver") -eq 1 || \
> > > + _notrun "need fio >= $req_ver (found $fio_ver)"
> > > + ;;
> > > + *-)
> > > + req_ver=${req_ver%-}
> > > + test $(_math "$fio_ver <= $req_ver") -eq 1 || \
> > > + _notrun "need fio <= $req_ver (found $fio_ver)"
> > > + ;;
> > > + *)
> > > + req_ver=${req_ver%-}
> > > + test $(_math "$fio_ver == $req_ver") -eq 1 || \
> > > + _notrun "need fio = $req_ver (found $fio_ver)"
> > > + ;;
> > > + esac
> > > +}
> > > +
> > > ################################################################################
> > > # make sure this script returns success
> > > /bin/true
> > > --
> > > 2.49.0
> > >
> >
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc
2025-08-22 8:02 ` [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc Ojaswin Mujoo
@ 2025-08-28 15:09 ` Darrick J. Wong
2025-09-02 15:52 ` John Garry
1 sibling, 0 replies; 31+ messages in thread
From: Darrick J. Wong @ 2025-08-28 15:09 UTC (permalink / raw)
To: Ojaswin Mujoo
Cc: Zorro Lang, fstests, Ritesh Harjani, john.g.garry, tytso,
linux-xfs, linux-kernel, linux-ext4
On Fri, Aug 22, 2025 at 01:32:09PM +0530, Ojaswin Mujoo wrote:
> From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
>
> This test does a lot of parallel RWF_ATOMIC IO on a preallocated file to
> stress the write and end-io unwritten conversion code paths. We brute
> force this for different blocksize and clustersizes and after each
> iteration we ensure the data was not torn or corrupted using fio crc
> verification.
>
> Note that in this test we use overlapping atomic writes of same io size.
> Although serializing racing writes is not guaranteed for RWF_ATOMIC,
> NVMe and SCSI provide this guarantee as an inseparable feature to
> power-fail atomicity. Keeping the iosize as same also ensures that ext4
> doesn't tear the write due to racing ioend unwritten conversion.
>
> The value of this test is that we make sure the RWF_ATOMIC is handled
> correctly by ext4 as well as test that the block layer doesn't split or
> only generate multiple bios for an atomic write.
>
> Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Looks good to me!
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> ---
> tests/ext4/061 | 155 +++++++++++++++++++++++++++++++++++++++++++++
> tests/ext4/061.out | 2 +
> 2 files changed, 157 insertions(+)
> create mode 100755 tests/ext4/061
> create mode 100644 tests/ext4/061.out
>
> diff --git a/tests/ext4/061 b/tests/ext4/061
> new file mode 100755
> index 00000000..0ccf9f69
> --- /dev/null
> +++ b/tests/ext4/061
> @@ -0,0 +1,155 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
> +#
> +# FS QA Test 061
> +#
> +# This test does a lot of parallel RWF_ATOMIC IO on a preallocated file to
> +# stress the write and end-io unwritten conversion code paths. We brute force
> +# this for all possible blocksize and clustersizes and after each iteration we
> +# ensure the data was not torn or corrupted using fio crc verification.
> +#
> +# Note that in this test we use overlapping atomic writes of same io size.
> +# Although serializing racing writes is not guaranteed for RWF_ATOMIC, NVMe and
> +# SCSI provide this guarantee as an inseparable feature to power-fail
> +# atomicity. Keeping the iosize as same also ensures that ext4 doesn't tear the
> +# write due to racing ioend unwritten conversion.
> +#
> +# The value of this test is that we make sure the RWF_ATOMIC is handled
> +# correctly by ext4 as well as test that the block layer doesn't split or only
> +# generate multiple bios for an atomic write.
> +
> +. ./common/preamble
> +. ./common/atomicwrites
> +
> +_begin_fstest auto rw stress atomicwrites
> +
> +_require_scratch_write_atomic
> +_require_aiodio
> +_require_fio_version "3.38+"
> +
> +FIO_LOAD=$(($(nproc) * 2 * LOAD_FACTOR))
> +SIZE=$((100*1024*1024))
> +
> +# Calculate fsblocksize as per bdev atomic write units.
> +bdev_awu_min=$(_get_atomic_write_unit_min $SCRATCH_DEV)
> +bdev_awu_max=$(_get_atomic_write_unit_max $SCRATCH_DEV)
> +bs=$(_max 4096 "$bdev_awu_min")
> +
> +function create_fio_configs()
> +{
> + local bsize=$1
> + create_fio_aw_config $bsize
> + create_fio_verify_config $bsize
> +}
> +
> +function create_fio_verify_config()
> +{
> + local bsize=$1
> +cat >$fio_verify_config <<EOF
> + [aio-dio-aw-verify]
> + direct=1
> + ioengine=libaio
> + rw=read
> + bs=$bsize
> + fallocate=native
> + filename=$SCRATCH_MNT/test-file
> + size=$SIZE
> + iodepth=$FIO_LOAD
> + numjobs=$FIO_LOAD
> + atomic=1
> + group_reporting=1
> +
> + verify_only=1
> + verify_state_save=0
> + verify=crc32c
> + verify_fatal=1
> + verify_write_sequence=0
> +EOF
> +}
> +
> +function create_fio_aw_config()
> +{
> + local bsize=$1
> +cat >$fio_aw_config <<EOF
> + [aio-dio-aw]
> + direct=1
> + ioengine=libaio
> + rw=randwrite
> + bs=$bsize
> + fallocate=native
> + filename=$SCRATCH_MNT/test-file
> + size=$SIZE
> + iodepth=$FIO_LOAD
> + numjobs=$FIO_LOAD
> + group_reporting=1
> + atomic=1
> +
> + verify_state_save=0
> + verify=crc32c
> + do_verify=0
> +
> +EOF
> +}
> +
> +run_test_one() {
> + local bs=$1
> + local cs=$2
> + local iosize=$3
> +
> + MKFS_OPTIONS="-O bigalloc -b $bs -C $cs"
> + _scratch_mkfs_ext4 >> $seqres.full 2>&1 || continue
> + if _try_scratch_mount >> $seqres.full 2>&1; then
> + echo "== Testing: bs=$bs cs=$cs iosize=$iosize ==" >> $seqres.full
> +
> + touch $SCRATCH_MNT/f1
> + create_fio_configs $iosize
> +
> + cat $fio_aw_config >> $seqres.full
> + echo >> $seqres.full
> + cat $fio_verify_config >> $seqres.full
> +
> + $FIO_PROG $fio_aw_config >> $seqres.full
> + ret1=$?
> +
> + $FIO_PROG $fio_verify_config >> $seqres.full
> + ret2=$?
> +
> + _scratch_unmount
> +
> + [[ $ret1 -eq 0 && $ret2 -eq 0 ]] || _fail "fio with atomic write failed"
> + fi
> +}
> +
> +run_test() {
> + local bs=$1
> +
> + # cluster sizes above 16 x blocksize are experimental so avoid them
> + # Also, cap cluster size at 128kb to keep it reasonable for large
> + # blocks size
> + max_cs=$(_min $((16 * bs)) "$bdev_awu_max" $((128 * 1024)))
> +
> + # Fuzz for combinations of blocksize, clustersize and
> + # iosize that cover most of the cases
> + run_test_one $bs $bs $bs
> + run_test_one $bs $max_cs $bs
> + run_test_one $bs $max_cs $max_cs
> + run_test_one $bs $max_cs $(_max "$((max_cs/2))" $bs)
> +}
> +
> +# Let's create a sample fio config to check whether fio supports all options.
> +fio_aw_config=$tmp.aw.fio
> +fio_verify_config=$tmp.verify.fio
> +fio_out=$tmp.fio.out
> +
> +create_fio_configs $bs
> +_require_fio $fio_aw_config
> +
> +for ((bs=$bs; bs <= $(_get_page_size); bs = $bs << 1)); do
> + run_test $bs
> +done
> +
> +# success, all done
> +echo Silence is golden
> +status=0
> +exit
> diff --git a/tests/ext4/061.out b/tests/ext4/061.out
> new file mode 100644
> index 00000000..273be9e0
> --- /dev/null
> +++ b/tests/ext4/061.out
> @@ -0,0 +1,2 @@
> +QA output created by 061
> +Silence is golden
> --
> 2.49.0
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-28 15:09 ` Darrick J. Wong
@ 2025-08-29 16:59 ` Ojaswin Mujoo
2025-08-30 17:09 ` Zorro Lang
0 siblings, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-08-29 16:59 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Zorro Lang, fstests, Ritesh Harjani, john.g.garry, tytso,
linux-xfs, linux-kernel, linux-ext4
On Thu, Aug 28, 2025 at 08:09:05AM -0700, Darrick J. Wong wrote:
> On Wed, Aug 27, 2025 at 08:46:34PM +0530, Ojaswin Mujoo wrote:
> > On Tue, Aug 26, 2025 at 12:08:01AM +0800, Zorro Lang wrote:
> > > On Fri, Aug 22, 2025 at 01:32:01PM +0530, Ojaswin Mujoo wrote:
> > > > The main motivation of adding this function on top of _require_fio is
> > > > that there has been a case in fio where atomic= option was added but
> > > > later it was changed to noop since kernel didn't yet have support for
> > > > atomic writes. It was then again utilized to do atomic writes in a later
> > > > version, once kernel got the support. Due to this there is a point in
> > > > fio where _require_fio w/ atomic=1 will succeed even though it would
> > > > not be doing atomic writes.
> > > >
> > > > Hence, add an explicit helper to ensure tests to require specific
> > > > versions of fio to work past such issues.
> > >
> > > Actually I'm wondering if fstests really needs to care about this. This's
> > > just a temporary issue of fio, not kernel or any fs usespace program. Do
> > > we need to add a seperated helper only for a temporary fio issue? If fio
> > > doesn't break fstests running, let it run. Just the testers install proper
> > > fio (maybe latest) they need. What do you and others think?
>
> Are there obvious failures if you try to run these new atomic write
> tests on a system with the weird versions of fio that have the no-op
> atomic= functionality? I'm concerned that some QA person is going to do
> that unwittingly and report that everything is ok when in reality they
> didn't actually test anything.
I think John has a bit more background but afaict, RWF_ATOMIC support
was added (fio commit: d01612f3ae25) but then removed (commit:
a25ba6c64fe1) since the feature didn't make it to kernel in time.
However the option seemed to be kept in place. Later, commit 40f1fc11d
added the support back in a later version of fio.
So yes, I think there are some version where fio will accept atomic=1
but not act upon it and the tests may start failing with no apparent
reason.
Regards,
ojaswin
>
> --D
>
> > > Thanks,
> > > Zorro
> >
> > Hey Zorro,
> >
> > Sure I'm okay with not keeping the helper and letting the user make sure
> > the fio version is correct.
> >
> > @John, does that sound okay?
> >
> > Regards,
> > ojaswin
> > >
> > > >
> > > > Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> > > > ---
> > > > common/rc | 32 ++++++++++++++++++++++++++++++++
> > > > 1 file changed, 32 insertions(+)
> > > >
> > > > diff --git a/common/rc b/common/rc
> > > > index 35a1c835..f45b9a38 100644
> > > > --- a/common/rc
> > > > +++ b/common/rc
> > > > @@ -5997,6 +5997,38 @@ _max() {
> > > > echo $ret
> > > > }
> > > >
> > > > +# Check the required fio version. Examples:
> > > > +# _require_fio_version 3.38 (matches 3.38 only)
> > > > +# _require_fio_version 3.38+ (matches 3.38 and above)
> > > > +# _require_fio_version 3.38- (matches 3.38 and below)
> > > > +_require_fio_version() {
> > > > + local req_ver="$1"
> > > > + local fio_ver
> > > > +
> > > > + _require_fio
> > > > + _require_math
> > > > +
> > > > + fio_ver=$(fio -v | cut -d"-" -f2)
> > > > +
> > > > + case "$req_ver" in
> > > > + *+)
> > > > + req_ver=${req_ver%+}
> > > > + test $(_math "$fio_ver >= $req_ver") -eq 1 || \
> > > > + _notrun "need fio >= $req_ver (found $fio_ver)"
> > > > + ;;
> > > > + *-)
> > > > + req_ver=${req_ver%-}
> > > > + test $(_math "$fio_ver <= $req_ver") -eq 1 || \
> > > > + _notrun "need fio <= $req_ver (found $fio_ver)"
> > > > + ;;
> > > > + *)
> > > > + req_ver=${req_ver%-}
> > > > + test $(_math "$fio_ver == $req_ver") -eq 1 || \
> > > > + _notrun "need fio = $req_ver (found $fio_ver)"
> > > > + ;;
> > > > + esac
> > > > +}
> > > > +
> > > > ################################################################################
> > > > # make sure this script returns success
> > > > /bin/true
> > > > --
> > > > 2.49.0
> > > >
> > >
> >
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-29 16:59 ` Ojaswin Mujoo
@ 2025-08-30 17:09 ` Zorro Lang
2025-09-01 11:40 ` Ojaswin Mujoo
2025-09-02 8:29 ` John Garry
0 siblings, 2 replies; 31+ messages in thread
From: Zorro Lang @ 2025-08-30 17:09 UTC (permalink / raw)
To: Ojaswin Mujoo
Cc: Darrick J. Wong, fstests, Ritesh Harjani, john.g.garry, tytso,
linux-xfs, linux-kernel, linux-ext4
On Fri, Aug 29, 2025 at 10:29:47PM +0530, Ojaswin Mujoo wrote:
> On Thu, Aug 28, 2025 at 08:09:05AM -0700, Darrick J. Wong wrote:
> > On Wed, Aug 27, 2025 at 08:46:34PM +0530, Ojaswin Mujoo wrote:
> > > On Tue, Aug 26, 2025 at 12:08:01AM +0800, Zorro Lang wrote:
> > > > On Fri, Aug 22, 2025 at 01:32:01PM +0530, Ojaswin Mujoo wrote:
> > > > > The main motivation of adding this function on top of _require_fio is
> > > > > that there has been a case in fio where atomic= option was added but
> > > > > later it was changed to noop since kernel didn't yet have support for
> > > > > atomic writes. It was then again utilized to do atomic writes in a later
> > > > > version, once kernel got the support. Due to this there is a point in
> > > > > fio where _require_fio w/ atomic=1 will succeed even though it would
> > > > > not be doing atomic writes.
> > > > >
> > > > > Hence, add an explicit helper to ensure tests to require specific
> > > > > versions of fio to work past such issues.
> > > >
> > > > Actually I'm wondering if fstests really needs to care about this. This's
> > > > just a temporary issue of fio, not kernel or any fs usespace program. Do
> > > > we need to add a seperated helper only for a temporary fio issue? If fio
> > > > doesn't break fstests running, let it run. Just the testers install proper
> > > > fio (maybe latest) they need. What do you and others think?
> >
> > Are there obvious failures if you try to run these new atomic write
> > tests on a system with the weird versions of fio that have the no-op
> > atomic= functionality? I'm concerned that some QA person is going to do
> > that unwittingly and report that everything is ok when in reality they
> > didn't actually test anything.
>
> I think John has a bit more background but afaict, RWF_ATOMIC support
> was added (fio commit: d01612f3ae25) but then removed (commit:
> a25ba6c64fe1) since the feature didn't make it to kernel in time.
> However the option seemed to be kept in place. Later, commit 40f1fc11d
> added the support back in a later version of fio.
>
> So yes, I think there are some version where fio will accept atomic=1
> but not act upon it and the tests may start failing with no apparent
> reason.
The concern from Darrick might be a problem. May I ask which fio commit
brought in this issue, and which fio commit fixed it? If this issue be
brought in and fixed within a fio release, it might be better. But if it
crosses fio release, that might be bad, then we might be better to have
this helper.
Thanks,
Zorro
>
> Regards,
> ojaswin
> >
> > --D
> >
> > > > Thanks,
> > > > Zorro
> > >
> > > Hey Zorro,
> > >
> > > Sure I'm okay with not keeping the helper and letting the user make sure
> > > the fio version is correct.
> > >
> > > @John, does that sound okay?
> > >
> > > Regards,
> > > ojaswin
> > > >
> > > > >
> > > > > Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> > > > > ---
> > > > > common/rc | 32 ++++++++++++++++++++++++++++++++
> > > > > 1 file changed, 32 insertions(+)
> > > > >
> > > > > diff --git a/common/rc b/common/rc
> > > > > index 35a1c835..f45b9a38 100644
> > > > > --- a/common/rc
> > > > > +++ b/common/rc
> > > > > @@ -5997,6 +5997,38 @@ _max() {
> > > > > echo $ret
> > > > > }
> > > > >
> > > > > +# Check the required fio version. Examples:
> > > > > +# _require_fio_version 3.38 (matches 3.38 only)
> > > > > +# _require_fio_version 3.38+ (matches 3.38 and above)
> > > > > +# _require_fio_version 3.38- (matches 3.38 and below)
> > > > > +_require_fio_version() {
> > > > > + local req_ver="$1"
> > > > > + local fio_ver
> > > > > +
> > > > > + _require_fio
> > > > > + _require_math
> > > > > +
> > > > > + fio_ver=$(fio -v | cut -d"-" -f2)
> > > > > +
> > > > > + case "$req_ver" in
> > > > > + *+)
> > > > > + req_ver=${req_ver%+}
> > > > > + test $(_math "$fio_ver >= $req_ver") -eq 1 || \
> > > > > + _notrun "need fio >= $req_ver (found $fio_ver)"
> > > > > + ;;
> > > > > + *-)
> > > > > + req_ver=${req_ver%-}
> > > > > + test $(_math "$fio_ver <= $req_ver") -eq 1 || \
> > > > > + _notrun "need fio <= $req_ver (found $fio_ver)"
> > > > > + ;;
> > > > > + *)
> > > > > + req_ver=${req_ver%-}
> > > > > + test $(_math "$fio_ver == $req_ver") -eq 1 || \
> > > > > + _notrun "need fio = $req_ver (found $fio_ver)"
> > > > > + ;;
> > > > > + esac
> > > > > +}
> > > > > +
> > > > > ################################################################################
> > > > > # make sure this script returns success
> > > > > /bin/true
> > > > > --
> > > > > 2.49.0
> > > > >
> > > >
> > >
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-30 17:09 ` Zorro Lang
@ 2025-09-01 11:40 ` Ojaswin Mujoo
2025-09-02 5:30 ` Zorro Lang
2025-09-02 8:29 ` John Garry
1 sibling, 1 reply; 31+ messages in thread
From: Ojaswin Mujoo @ 2025-09-01 11:40 UTC (permalink / raw)
To: Zorro Lang
Cc: Darrick J. Wong, fstests, Ritesh Harjani, john.g.garry, tytso,
linux-xfs, linux-kernel, linux-ext4
On Sun, Aug 31, 2025 at 01:09:07AM +0800, Zorro Lang wrote:
> On Fri, Aug 29, 2025 at 10:29:47PM +0530, Ojaswin Mujoo wrote:
> > On Thu, Aug 28, 2025 at 08:09:05AM -0700, Darrick J. Wong wrote:
> > > On Wed, Aug 27, 2025 at 08:46:34PM +0530, Ojaswin Mujoo wrote:
> > > > On Tue, Aug 26, 2025 at 12:08:01AM +0800, Zorro Lang wrote:
> > > > > On Fri, Aug 22, 2025 at 01:32:01PM +0530, Ojaswin Mujoo wrote:
> > > > > > The main motivation of adding this function on top of _require_fio is
> > > > > > that there has been a case in fio where atomic= option was added but
> > > > > > later it was changed to noop since kernel didn't yet have support for
> > > > > > atomic writes. It was then again utilized to do atomic writes in a later
> > > > > > version, once kernel got the support. Due to this there is a point in
> > > > > > fio where _require_fio w/ atomic=1 will succeed even though it would
> > > > > > not be doing atomic writes.
> > > > > >
> > > > > > Hence, add an explicit helper to ensure tests to require specific
> > > > > > versions of fio to work past such issues.
> > > > >
> > > > > Actually I'm wondering if fstests really needs to care about this. This's
> > > > > just a temporary issue of fio, not kernel or any fs usespace program. Do
> > > > > we need to add a seperated helper only for a temporary fio issue? If fio
> > > > > doesn't break fstests running, let it run. Just the testers install proper
> > > > > fio (maybe latest) they need. What do you and others think?
> > >
> > > Are there obvious failures if you try to run these new atomic write
> > > tests on a system with the weird versions of fio that have the no-op
> > > atomic= functionality? I'm concerned that some QA person is going to do
> > > that unwittingly and report that everything is ok when in reality they
> > > didn't actually test anything.
> >
> > I think John has a bit more background but afaict, RWF_ATOMIC support
> > was added (fio commit: d01612f3ae25) but then removed (commit:
> > a25ba6c64fe1) since the feature didn't make it to kernel in time.
> > However the option seemed to be kept in place. Later, commit 40f1fc11d
> > added the support back in a later version of fio.
> >
> > So yes, I think there are some version where fio will accept atomic=1
> > but not act upon it and the tests may start failing with no apparent
> > reason.
>
> The concern from Darrick might be a problem. May I ask which fio commit
> brought in this issue, and which fio commit fixed it? If this issue be
> brought in and fixed within a fio release, it might be better. But if it
> crosses fio release, that might be bad, then we might be better to have
> this helper.
Hi Zorro, yes it does seem to cross version boundaries. The
functionality was removed in fio v3.33 and added back in v3.38. I
confirmed this by running generic/1226 with both (for v3.33 run i
commented out a few fio options that were added later but kept
atomic=1):
Command: sudo perf record -e iomap:iomap_dio_rw_begin ./check generic/1226
perf script sample with fio v3.33:
fio 6626 [000] 777.668017: iomap:iomap_dio_rw_begin: <.sniip.> flags DIRECT|WRITE|AIO_RW dio_flags aio 1
perf script sample with fio v3.39:
fio 9830 [000] 895.042747: iomap:iomap_dio_rw_begin: <.snip> flags ATOMIC|DIRECT|WRITE|AIO_RW dio_flags aio 1
So as we can see, even though the test passes with atomic=1, fio is not
sending the RWF_ATOMIC flag in the older version.
In which case I believe it should be okay to keep the helper, right?
Thanks,
Ojaswin
>
> Thanks,
> Zorro
>
> >
> > Regards,
> > ojaswin
> > >
> > > --D
> > >
> > > > > Thanks,
> > > > > Zorro
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-09-01 11:40 ` Ojaswin Mujoo
@ 2025-09-02 5:30 ` Zorro Lang
0 siblings, 0 replies; 31+ messages in thread
From: Zorro Lang @ 2025-09-02 5:30 UTC (permalink / raw)
To: Ojaswin Mujoo
Cc: Darrick J. Wong, fstests, Ritesh Harjani, john.g.garry, tytso,
linux-xfs, linux-kernel, linux-ext4
On Mon, Sep 01, 2025 at 05:10:01PM +0530, Ojaswin Mujoo wrote:
> On Sun, Aug 31, 2025 at 01:09:07AM +0800, Zorro Lang wrote:
> > On Fri, Aug 29, 2025 at 10:29:47PM +0530, Ojaswin Mujoo wrote:
> > > On Thu, Aug 28, 2025 at 08:09:05AM -0700, Darrick J. Wong wrote:
> > > > On Wed, Aug 27, 2025 at 08:46:34PM +0530, Ojaswin Mujoo wrote:
> > > > > On Tue, Aug 26, 2025 at 12:08:01AM +0800, Zorro Lang wrote:
> > > > > > On Fri, Aug 22, 2025 at 01:32:01PM +0530, Ojaswin Mujoo wrote:
> > > > > > > The main motivation of adding this function on top of _require_fio is
> > > > > > > that there has been a case in fio where atomic= option was added but
> > > > > > > later it was changed to noop since kernel didn't yet have support for
> > > > > > > atomic writes. It was then again utilized to do atomic writes in a later
> > > > > > > version, once kernel got the support. Due to this there is a point in
> > > > > > > fio where _require_fio w/ atomic=1 will succeed even though it would
> > > > > > > not be doing atomic writes.
> > > > > > >
> > > > > > > Hence, add an explicit helper to ensure tests to require specific
> > > > > > > versions of fio to work past such issues.
> > > > > >
> > > > > > Actually I'm wondering if fstests really needs to care about this. This's
> > > > > > just a temporary issue of fio, not kernel or any fs usespace program. Do
> > > > > > we need to add a seperated helper only for a temporary fio issue? If fio
> > > > > > doesn't break fstests running, let it run. Just the testers install proper
> > > > > > fio (maybe latest) they need. What do you and others think?
> > > >
> > > > Are there obvious failures if you try to run these new atomic write
> > > > tests on a system with the weird versions of fio that have the no-op
> > > > atomic= functionality? I'm concerned that some QA person is going to do
> > > > that unwittingly and report that everything is ok when in reality they
> > > > didn't actually test anything.
> > >
> > > I think John has a bit more background but afaict, RWF_ATOMIC support
> > > was added (fio commit: d01612f3ae25) but then removed (commit:
> > > a25ba6c64fe1) since the feature didn't make it to kernel in time.
> > > However the option seemed to be kept in place. Later, commit 40f1fc11d
> > > added the support back in a later version of fio.
> > >
> > > So yes, I think there are some version where fio will accept atomic=1
> > > but not act upon it and the tests may start failing with no apparent
> > > reason.
> >
> > The concern from Darrick might be a problem. May I ask which fio commit
> > brought in this issue, and which fio commit fixed it? If this issue be
> > brought in and fixed within a fio release, it might be better. But if it
> > crosses fio release, that might be bad, then we might be better to have
> > this helper.
>
> Hi Zorro, yes it does seem to cross version boundaries. The
> functionality was removed in fio v3.33 and added back in v3.38. I
Thanks, if so I think let's have this helper for that issue :) But I think
we still prioritize _require_fio. If it helpless, then call _require_fio_version.
Reviewed-by: Zorro Lang <zlang@redhat.com>
Thanks,
Zorro
> confirmed this by running generic/1226 with both (for v3.33 run i
> commented out a few fio options that were added later but kept
> atomic=1):
>
> Command: sudo perf record -e iomap:iomap_dio_rw_begin ./check generic/1226
>
> perf script sample with fio v3.33:
>
> fio 6626 [000] 777.668017: iomap:iomap_dio_rw_begin: <.sniip.> flags DIRECT|WRITE|AIO_RW dio_flags aio 1
>
> perf script sample with fio v3.39:
>
> fio 9830 [000] 895.042747: iomap:iomap_dio_rw_begin: <.snip> flags ATOMIC|DIRECT|WRITE|AIO_RW dio_flags aio 1
>
> So as we can see, even though the test passes with atomic=1, fio is not
> sending the RWF_ATOMIC flag in the older version.
>
> In which case I believe it should be okay to keep the helper, right?
>
> Thanks,
> Ojaswin
>
> >
> > Thanks,
> > Zorro
> >
> > >
> > > Regards,
> > > ojaswin
> > > >
> > > > --D
> > > >
> > > > > > Thanks,
> > > > > > Zorro
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-30 17:09 ` Zorro Lang
2025-09-01 11:40 ` Ojaswin Mujoo
@ 2025-09-02 8:29 ` John Garry
1 sibling, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 8:29 UTC (permalink / raw)
To: Zorro Lang, Ojaswin Mujoo
Cc: Darrick J. Wong, fstests, Ritesh Harjani, tytso, linux-xfs,
linux-kernel, linux-ext4
On 30/08/2025 18:09, Zorro Lang wrote:
>> I think John has a bit more background but afaict, RWF_ATOMIC support
>> was added (fio commit: d01612f3ae25) but then removed (commit:
>> a25ba6c64fe1) since the feature didn't make it to kernel in time.
>> However the option seemed to be kept in place. Later, commit 40f1fc11d
>> added the support back in a later version of fio.
>>
>> So yes, I think there are some version where fio will accept atomic=1
>> but not act upon it and the tests may start failing with no apparent
>> reason.
> The concern from Darrick might be a problem. May I ask which fio commit
> brought in this issue, and which fio commit fixed it? If this issue be
> brought in and fixed within a fio release, it might be better. But if it
> crosses fio release, that might be bad, then we might be better to have
> this helper.
The history is that fio atomic write support was originally added some
time ago for out-of-kernel atomic write support, which was O_ATOMIC
flag. Since O_ATOMIC never made it into the kernel, the feature was
removed, but the plumbing for atomic writes stayed in fio - specifically
the "atomic=" option. So I just reused that plumbing in d01612f3ae25 to
support RWF_ATOMIC.
The point is that we should check the fio version, as different versions
can give different behaviour for "atomic" option, those being:
a. O_ATOMIC (we definitely don't want this)
b. no nothing (bad)
c. use RWF_ATOMIC
Thanks,
John
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 02/12] common/rc: Add _require_fio_version helper
2025-08-22 8:02 ` [PATCH v5 02/12] common/rc: Add _require_fio_version helper Ojaswin Mujoo
2025-08-25 16:08 ` Zorro Lang
@ 2025-09-02 14:50 ` John Garry
1 sibling, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 14:50 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> The main motivation of adding this function on top of _require_fio is
> that there has been a case in fio where atomic= option was added but
> later it was changed to noop since kernel didn't yet have support for
> atomic writes. It was then again utilized to do atomic writes in a later
> version, once kernel got the support. Due to this there is a point in
> fio where _require_fio w/ atomic=1 will succeed even though it would
> not be doing atomic writes.
>
> Hence, add an explicit helper to ensure tests to require specific
> versions of fio to work past such issues.
>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> ---
> common/rc | 32 ++++++++++++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/common/rc b/common/rc
> index 35a1c835..f45b9a38 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -5997,6 +5997,38 @@ _max() {
> echo $ret
> }
>
> +# Check the required fio version. Examples:
> +# _require_fio_version 3.38 (matches 3.38 only)
> +# _require_fio_version 3.38+ (matches 3.38 and above)
> +# _require_fio_version 3.38- (matches 3.38 and below)
This requires the user to know the version which corresponds to the
feature. Is that how things are done for other such utilities and their
versions vs features?
I was going to suggest exporting something like
_require_fio_atomic_writes(), and _require_fio_atomic_writes() calls
_require_fio_version() to check the version.
Thanks,
John
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 04/12] ltp/fsx.c: Add atomic writes support to fsx
2025-08-22 8:02 ` [PATCH v5 04/12] ltp/fsx.c: Add atomic writes support to fsx Ojaswin Mujoo
@ 2025-09-02 15:06 ` John Garry
0 siblings, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:06 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> Implement atomic write support to help fuzz atomic writes
> with fsx.
>
> Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Generally this looks ok, but I do have some comments, so please check them:
Reviewed-by: John Garry <john.g.garry@oracle.com>
> ---
> ltp/fsx.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 110 insertions(+), 5 deletions(-)
>
> diff --git a/ltp/fsx.c b/ltp/fsx.c
> index 163b9453..1582f6d1 100644
> --- a/ltp/fsx.c
> +++ b/ltp/fsx.c
> @@ -40,6 +40,7 @@
> #include <liburing.h>
> #endif
> #include <sys/syscall.h>
> +#include "statx.h"
>
> #ifndef MAP_FILE
> # define MAP_FILE 0
> @@ -49,6 +50,10 @@
> #define RWF_DONTCACHE 0x80
> #endif
>
> +#ifndef RWF_ATOMIC
> +#define RWF_ATOMIC 0x40
> +#endif
> +
> #define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */
>
> /* Operation flags (bitmask) */
> @@ -110,6 +115,7 @@ enum {
> OP_READ_DONTCACHE,
> OP_WRITE,
> OP_WRITE_DONTCACHE,
> + OP_WRITE_ATOMIC,
> OP_MAPREAD,
> OP_MAPWRITE,
> OP_MAX_LITE,
> @@ -200,6 +206,11 @@ int uring = 0;
> int mark_nr = 0;
> int dontcache_io = 1;
> int hugepages = 0; /* -h flag */
> +int do_atomic_writes = 1; /* -a flag disables */
> +
> +/* User for atomic writes */
> +int awu_min = 0;
> +int awu_max = 0;
>
> /* Stores info needed to periodically collapse hugepages */
> struct hugepages_collapse_info {
> @@ -288,6 +299,7 @@ static const char *op_names[] = {
> [OP_READ_DONTCACHE] = "read_dontcache",
> [OP_WRITE] = "write",
> [OP_WRITE_DONTCACHE] = "write_dontcache",
> + [OP_WRITE_ATOMIC] = "write_atomic",
> [OP_MAPREAD] = "mapread",
> [OP_MAPWRITE] = "mapwrite",
> [OP_TRUNCATE] = "truncate",
> @@ -422,6 +434,7 @@ logdump(void)
> prt("\t***RRRR***");
> break;
> case OP_WRITE_DONTCACHE:
> + case OP_WRITE_ATOMIC:
> case OP_WRITE:
> prt("WRITE 0x%x thru 0x%x\t(0x%x bytes)",
> lp->args[0], lp->args[0] + lp->args[1] - 1,
> @@ -1073,6 +1086,25 @@ update_file_size(unsigned offset, unsigned size)
> file_size = offset + size;
> }
>
> +static int is_power_of_2(unsigned n) {
> + return ((n & (n - 1)) == 0);
> +}
> +
> +/*
> + * Round down n to nearest power of 2.
> + * If n is already a power of 2, return n;
> + */
> +static int rounddown_pow_of_2(int n) {
> + int i = 0;
> +
> + if (is_power_of_2(n))
> + return n;
> +
> + for (; (1 << i) < n; i++);
> +
> + return 1 << (i - 1);
Is this the neatest way to do this?
> +}
> +
> void
> dowrite(unsigned offset, unsigned size, int flags)
> {
> @@ -1081,6 +1113,27 @@ dowrite(unsigned offset, unsigned size, int flags)
> offset -= offset % writebdy;
> if (o_direct)
> size -= size % writebdy;
> + if (flags & RWF_ATOMIC) {
> + /* atomic write len must be inbetween awu_min and awu_max */
in between
> + if (size < awu_min)
> + size = awu_min;
> + if (size > awu_max)
> + size = awu_max;
> +
> + /* atomic writes need power-of-2 sizes */
> + size = rounddown_pow_of_2(size);
you could have:
if (size < awu_min)
size = awu_min;
else if (size > awu_max)
size = awu_max;
else
size = rounddown_pow_of_2(size);
> +
> + /* atomic writes need naturally aligned offsets */
> + offset -= offset % size;
> +
> + /* Skip the write if we are crossing max filesize */
> + if ((offset + size) > maxfilelen) {
> + if (!quiet && testcalls > simulatedopcount)
> + prt("skipping atomic write past maxfilelen\n");
> + log4(OP_WRITE_ATOMIC, offset, size, FL_SKIPPED);
> + return;
> + }
> + }
> if (size == 0) {
> if (!quiet && testcalls > simulatedopcount && !o_direct)
> prt("skipping zero size write\n");
> @@ -1088,7 +1141,10 @@ dowrite(unsigned offset, unsigned size, int flags)
> return;
> }
>
> - log4(OP_WRITE, offset, size, FL_NONE);
> + if (flags & RWF_ATOMIC)
> + log4(OP_WRITE_ATOMIC, offset, size, FL_NONE);
> + else
> + log4(OP_WRITE, offset, size, FL_NONE);
>
> gendata(original_buf, good_buf, offset, size);
> if (offset + size > file_size) {
> @@ -1108,8 +1164,9 @@ dowrite(unsigned offset, unsigned size, int flags)
> (monitorstart == -1 ||
> (offset + size > monitorstart &&
> (monitorend == -1 || offset <= monitorend))))))
> - prt("%lld write\t0x%x thru\t0x%x\t(0x%x bytes)\tdontcache=%d\n", testcalls,
> - offset, offset + size - 1, size, (flags & RWF_DONTCACHE) != 0);
> + prt("%lld write\t0x%x thru\t0x%x\t(0x%x bytes)\tdontcache=%d atomic_wr=%d\n", testcalls,
> + offset, offset + size - 1, size, (flags & RWF_DONTCACHE) != 0,
> + (flags & RWF_ATOMIC) != 0);
nit:
!!(flags & RWF_ATOMIC)
I find that a bit neater, but I suppose you are following the example
for RWF_DONTCACHE
> iret = fsxwrite(fd, good_buf + offset, size, offset, flags);
> if (iret != size) {
> if (iret == -1)
> @@ -1785,6 +1842,36 @@ do_dedupe_range(unsigned offset, unsigned length, unsigned dest)
> }
> #endif
>
> +int test_atomic_writes(void) {
> + int ret;
> + struct statx stx;
> +
> + if (o_direct != O_DIRECT) {
> + fprintf(stderr, "main: atomic writes need O_DIRECT (-Z), "
> + "disabling!\n");
> + return 0;
> + }
> +
> + ret = xfstests_statx(AT_FDCWD, fname, 0, STATX_WRITE_ATOMIC, &stx);
> + if (ret < 0) {
> + fprintf(stderr, "main: Statx failed with %d."
> + " Failed to determine atomic write limits, "
> + " disabling!\n", ret);
> + return 0;
> + }
> +
> + if (stx.stx_attributes & STATX_ATTR_WRITE_ATOMIC &&
> + stx.stx_atomic_write_unit_min > 0) {
> + awu_min = stx.stx_atomic_write_unit_min;
> + awu_max = stx.stx_atomic_write_unit_max;
> + return 1;
> + }
> +
> + fprintf(stderr, "main: IO Stack does not support "
> + "atomic writes, disabling!\n");
Do we really need to spread this over multiple lines?
Maybe that is the coding standard - I don't know.
> + return 0;
> +}
> +
> #ifdef HAVE_COPY_FILE_RANGE
> int
> test_copy_range(void)
> @@ -2356,6 +2443,12 @@ have_op:
> goto out;
> }
> break;
> + case OP_WRITE_ATOMIC:
> + if (!do_atomic_writes) {
> + log4(OP_WRITE_ATOMIC, offset, size, FL_SKIPPED);
> + goto out;
> + }
> + break;
> }
>
> switch (op) {
> @@ -2385,6 +2478,11 @@ have_op:
> dowrite(offset, size, 0);
> break;
>
> + case OP_WRITE_ATOMIC:
> + TRIM_OFF_LEN(offset, size, maxfilelen);
> + dowrite(offset, size, RWF_ATOMIC);
> + break;
> +
> case OP_MAPREAD:
> TRIM_OFF_LEN(offset, size, file_size);
> domapread(offset, size);
> @@ -2511,13 +2609,14 @@ void
> usage(void)
> {
> fprintf(stdout, "usage: %s",
> - "fsx [-dfhknqxyzBEFHIJKLORWXZ0]\n\
> + "fsx [-adfhknqxyzBEFHIJKLORWXZ0]\n\
> [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid]\n\
> [-l flen] [-m start:end] [-o oplen] [-p progressinterval]\n\
> [-r readbdy] [-s style] [-t truncbdy] [-w writebdy]\n\
> [-A|-U] [-D startingop] [-N numops] [-P dirpath] [-S seed]\n\
> [--replay-ops=opsfile] [--record-ops[=opsfile]] [--duration=seconds]\n\
> ... fname\n\
> + -a: disable atomic writes\n\
> -b opnum: beginning operation number (default 1)\n\
> -c P: 1 in P chance of file close+open at each op (default infinity)\n\
> -d: debug output for all operations\n\
> @@ -3059,9 +3158,13 @@ main(int argc, char **argv)
> setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
>
> while ((ch = getopt_long(argc, argv,
> - "0b:c:de:fg:hi:j:kl:m:no:p:qr:s:t:uw:xyABD:EFJKHzCILN:OP:RS:UWXZ",
> + "0ab:c:de:fg:hi:j:kl:m:no:p:qr:s:t:uw:xyABD:EFJKHzCILN:OP:RS:UWXZ",
> longopts, NULL)) != EOF)
> switch (ch) {
> + case 'a':
> + prt("main(): Atomic writes disabled\n");
> + do_atomic_writes = 0;
why an opt-out (and not opt-in)?
> + break;
> case 'b':
> simulatedopcount = getnum(optarg, &endp);
> if (!quiet)
> @@ -3475,6 +3578,8 @@ main(int argc, char **argv)
> exchange_range_calls = test_exchange_range();
> if (dontcache_io)
> dontcache_io = test_dontcache_io();
> + if (do_atomic_writes)
> + do_atomic_writes = test_atomic_writes();
>
> while (keep_running())
> if (!test())
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 05/12] generic: Add atomic write test using fio crc check verifier
2025-08-22 8:02 ` [PATCH v5 05/12] generic: Add atomic write test using fio crc check verifier Ojaswin Mujoo
@ 2025-09-02 15:09 ` John Garry
0 siblings, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:09 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> This adds atomic write test using fio based on it's crc check verifier.
> fio adds a crc header for each data block, which is verified later to
> ensure there is no data corruption or torn write.
>
> This test essentially does a lot of parallel RWF_ATOMIC IO on a
> preallocated file to stress the write and end-io unwritten conversion
> code paths. The idea is to increase code coverage to ensure RWF_ATOMIC
> hasn't introduced any issues.
>
> Avoid doing overlapping parallel atomic writes because it might give
> unexpected results. Use offset_increment=, size= fio options to achieve
> this behavior.
>
> Co-developed-by: Ritesh Harjani (IBM)<ritesh.list@gmail.com>
> Signed-off-by: Ritesh Harjani (IBM)<ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong<djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo<ojaswin@linux.ibm.com>
I am still a bit skeptical of the value of this test, but here is my tag
anyway:
Reviewed-by: John Garry <john.g.garry@oracle.com>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 06/12] generic: Add atomic write test using fio verify on file mixed mappings
2025-08-22 8:02 ` [PATCH v5 06/12] generic: Add atomic write test using fio verify on file mixed mappings Ojaswin Mujoo
@ 2025-09-02 15:10 ` John Garry
0 siblings, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:10 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> This test uses fio to first create a file with mixed mappings. Then it
> does atomic writes using aio dio with parallel jobs to the same file
> with mixed mappings. Finally, we perform a fio verify step to ensure
> there is no data corruption or torn write.
>
> The aim is to stress the FS block allocation and extent handling logic
> to ensure it handles mixed mappings with RWF_ATOMIC correctly without
> tearing or losing data.
>
> Avoid doing overlapping parallel atomic writes because it might give
> unexpected results. Use offset_increment=, size= fio options to achieve
> this behavior.
>
> Co-developed-by: Ritesh Harjani (IBM)<ritesh.list@gmail.com>
> Signed-off-by: Ritesh Harjani (IBM)<ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong<djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo<ojaswin@linux.ibm.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 07/12] generic: Add atomic write multi-fsblock O_[D]SYNC tests
2025-08-22 8:02 ` [PATCH v5 07/12] generic: Add atomic write multi-fsblock O_[D]SYNC tests Ojaswin Mujoo
@ 2025-09-02 15:14 ` John Garry
0 siblings, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:14 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> This adds various atomic write multi-fsblock stresst tests
stress
> with mixed mappings and O_SYNC, to ensure the data and metadata
> is atomically persisted even if there is a shutdown.
>
> Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Regardless of any comments made (which are minor):
Reviewed-by: John Garry <john.g.garry@oracle.com>
> ---
> tests/generic/1228 | 137 +++++++++++++++++++++++++++++++++++++++++
> tests/generic/1228.out | 2 +
> 2 files changed, 139 insertions(+)
> create mode 100755 tests/generic/1228
> create mode 100644 tests/generic/1228.out
>
> diff --git a/tests/generic/1228 b/tests/generic/1228
> new file mode 100755
> index 00000000..888599ce
> --- /dev/null
> +++ b/tests/generic/1228
> @@ -0,0 +1,137 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
> +#
> +# FS QA Test 1228
> +#
> +# Atomic write multi-fsblock data integrity tests with mixed mappings
> +# and O_SYNC
> +#
> +. ./common/preamble
> +. ./common/atomicwrites
> +_begin_fstest auto quick rw atomicwrites
> +
> +_require_scratch_write_atomic_multi_fsblock
> +_require_atomic_write_test_commands
> +_require_scratch_shutdown
> +_require_xfs_io_command "truncate"
> +
> +_scratch_mkfs >> $seqres.full
> +_scratch_mount >> $seqres.full
> +
> +check_data_integrity() {
> + actual=$(_hexdump $testfile)
> + if [[ "$expected" != "$actual" ]]
> + then
> + echo "Integrity check failed"
> + echo "Integrity check failed" >> $seqres.full
> + echo "# Expected file contents:" >> $seqres.full
> + echo "$expected" >> $seqres.full
> + echo "# Actual file contents:" >> $seqres.full
> + echo "$actual" >> $seqres.full
> +
> + _fail "Data integrity check failed. The atomic write was torn."
> + fi
> +}
> +
> +prep_mixed_mapping() {
> + $XFS_IO_PROG -c "truncate 0" $testfile >> $seqres.full
> + local off=0
> + local mapping=""
> +
> + local operations=("W" "H" "U")
> + local num_blocks=$((awu_max / blksz))
> + for ((i=0; i<num_blocks; i++)); do
> + local index=$((RANDOM % ${#operations[@]}))
> + local map="${operations[$index]}"
> + local mapping="${mapping}${map}"
> +
> + case "$map" in
> + "W")
> + $XFS_IO_PROG -dc "pwrite -S 0x61 -b $blksz $off $blksz" $testfile > /dev/null
> + ;;
> + "H")
> + # No operation needed for hole
> + ;;
> + "U")
> + $XFS_IO_PROG -c "falloc $off $blksz" $testfile >> /dev/null
> + ;;
> + esac
> + off=$((off + blksz))
> + done
> +
> + echo "+ + Mixed mapping prep done. Full mapping pattern: $mapping" >> $seqres.full
> +
> + sync $testfile
> +}
> +
> +verify_atomic_write() {
> + test $bytes_written -eq $awu_max || _fail "atomic write len=$awu_max assertion failed"
> + check_data_integrity
> +}
> +
> +mixed_mapping_test() {
> + prep_mixed_mapping
> +
> + echo -"+ + Performing O_DSYNC atomic write from 0 to $awu_max" >> $seqres.full
> + if [[ "$1" == "shutdown" ]]
> + then
> + bytes_written=$($XFS_IO_PROG -x -dc \
> + "pwrite -DA -V1 -b $awu_max 0 $awu_max" \
> + -c "shutdown" $testfile | grep wrote | \
> + awk -F'[/ ]' '{print $2}')
> + _scratch_cycle_mount >>$seqres.full 2>&1 || _fail "remount failed"
> + else
> + bytes_written=$($XFS_IO_PROG -dc \
> + "pwrite -DA -V1 -b $awu_max 0 $awu_max" $testfile | \
> + grep wrote | awk -F'[/ ]' '{print $2}')
> + fi
> +
> + verify_atomic_write
> +}
> +
> +testfile=$SCRATCH_MNT/testfile
> +touch $testfile
> +
> +awu_max=$(_get_atomic_write_unit_max $testfile)
> +blksz=$(_get_block_size $SCRATCH_MNT)
> +
> +# Create an expected pattern to compare with
> +$XFS_IO_PROG -tc "pwrite -b $awu_max 0 $awu_max" $testfile >> $seqres.full
> +expected=$(_hexdump $testfile)
> +echo "# Expected file contents:" >> $seqres.full
> +echo "$expected" >> $seqres.full
> +echo >> $seqres.full
> +
> +echo "# Test 1: Do O_DSYNC atomic write on random mixed mapping:" >> $seqres.full
> +echo >> $seqres.full
> +for ((iteration=1; iteration<=10; iteration++)); do
> + echo "=== Mixed Mapping Test Iteration $iteration ===" >> $seqres.full
> +
> + echo "+ Testing without shutdown..." >> $seqres.full
> + mixed_mapping_test
> + echo "Passed!" >> $seqres.full
> +
> + echo "+ Testing with sudden shutdown..." >> $seqres.full
> + mixed_mapping_test "shutdown"
> + echo "Passed!" >> $seqres.full
> +
> + echo "Iteration $iteration completed: OK" >> $seqres.full
> + echo >> $seqres.full
> +done
> +echo "# Test 1: Do O_SYNC atomic write on random mixed mapping (10 iterations): OK" >> $seqres.full
big nit: you should keep the loop count in a global variable
> +
> +
> +echo >> $seqres.full
> +echo "# Test 2: Do extending O_SYNC atomic writes: " >> $seqres.full
> +bytes_written=$($XFS_IO_PROG -x -dstc "pwrite -A -V1 -b $awu_max 0 $awu_max" \
> + -c "shutdown" $testfile | grep wrote | awk -F'[/ ]' '{print $2}')
> +_scratch_cycle_mount >>$seqres.full 2>&1 || _fail "remount failed"
> +verify_atomic_write
> +echo "# Test 2: Do extending O_SYNC atomic writes: OK" >> $seqres.full
> +
> +# success, all done
> +echo "Silence is golden"
> +status=0
> +exit
> +
> diff --git a/tests/generic/1228.out b/tests/generic/1228.out
> new file mode 100644
> index 00000000..1baffa91
> --- /dev/null
> +++ b/tests/generic/1228.out
> @@ -0,0 +1,2 @@
> +QA output created by 1228
> +Silence is golden
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 08/12] generic: Stress fsx with atomic writes enabled
2025-08-22 8:02 ` [PATCH v5 08/12] generic: Stress fsx with atomic writes enabled Ojaswin Mujoo
@ 2025-09-02 15:18 ` John Garry
0 siblings, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:18 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> Stress file with atomic writes to ensure we excercise codepaths
exercise
> where we are mixing different FS operations with atomic writes
>
> Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
> ---
> tests/generic/1229 | 68 ++++++++++++++++++++++++++++++++++++++++++
> tests/generic/1229.out | 2 ++
> 2 files changed, 70 insertions(+)
> create mode 100755 tests/generic/1229
> create mode 100644 tests/generic/1229.out
>
> diff --git a/tests/generic/1229 b/tests/generic/1229
> new file mode 100755
> index 00000000..7fa57105
> --- /dev/null
> +++ b/tests/generic/1229
> @@ -0,0 +1,68 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
> +#
> +# FS QA Test 1229
> +#
> +# fuzz fsx with atomic writes
> +#
> +. ./common/preamble
> +. ./common/atomicwrites
> +_begin_fstest rw auto quick atomicwrites
> +
> +_require_odirect
> +_require_scratch_write_atomic
> +
> +_scratch_mkfs >> $seqres.full 2>&1
> +_scratch_mount >> $seqres.full 2>&1
> +
> +testfile=$SCRATCH_MNT/testfile
> +touch $testfile
> +
> +awu_max=$(_get_atomic_write_unit_max $testfile)
> +blksz=$(_get_block_size $SCRATCH_MNT)
> +bsize=`$here/src/min_dio_alignment $SCRATCH_MNT $SCRATCH_DEV`
> +
> +set_fsx_avoid() {
> + local file=$1
> +
> + case "$FSTYP" in
> + "ext4")
> + local dev=$(findmnt -n -o SOURCE --target $testfile)
> +
> + # fsx insert/collpase range support for ext4+bigalloc is
> + # currently broken, so disable it. Also disable incase we can't
in case
> + # detect bigalloc to be on safer side.
> + if [ -z "$DUMPE2FS_PROG" ]; then
> + echo "dumpe2fs not found, disabling insert/collapse range" >> $seqres.full
> + FSX_AVOID+=" -I -C"
> + return
> + fi
> +
> + $DUMPE2FS_PROG -h $dev 2>&1 | grep -q bigalloc && {
> + echo "fsx insert/collapse range not supported with bigalloc. Disabling.." >> $seqres.full
> + FSX_AVOID+=" -I -C"
> + }
> + ;;
> + *)
> + ;;
> + esac
> +}
> +
> +# fsx usage:
> +#
> +# -N numops: total # operations to do
> +# -l flen: the upper bound on file size
> +# -o oplen: the upper bound on operation size (64k default)
> +# -Z: O_DIRECT ()
> +
> +set_fsx_avoid
> +_run_fsx_on_file $testfile -N 10000 -o $awu_max -A -l 500000 -r $bsize -w $bsize -Z $FSX_AVOID >> $seqres.full
> +if [[ "$?" != "0" ]]
> +then
> + _fail "fsx returned error: $?"
> +fi
> +
> +echo "Silence is golden"
> +status=0
> +exit
> diff --git a/tests/generic/1229.out b/tests/generic/1229.out
> new file mode 100644
> index 00000000..737d61c6
> --- /dev/null
> +++ b/tests/generic/1229.out
> @@ -0,0 +1,2 @@
> +QA output created by 1229
> +Silence is golden
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 09/12] generic: Add sudden shutdown tests for multi block atomic writes
2025-08-22 8:02 ` [PATCH v5 09/12] generic: Add sudden shutdown tests for multi block atomic writes Ojaswin Mujoo
@ 2025-09-02 15:49 ` John Garry
0 siblings, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:49 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> This test is intended to ensure that multi blocks atomic writes
> maintain atomic guarantees across sudden FS shutdowns.
>
> The way we work is that we lay out a file with random mix of written,
> unwritten and hole extents. Then we start performing atomic writes
> sequentially on the file while we parallely shutdown the FS. Then we
> note the last offset where the atomic write happened just before shut
> down and then make sure blocks around it either have completely old
> data or completely new data, ie the write was not torn during shutdown.
>
> We repeat the same with completely written, completely unwritten and completely
> empty file to ensure these cases are not torn either. Finally, we have a
> similar test for append atomic writes
>
> Suggested-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Please check comments, below, thanks!
> ---
> tests/generic/1230 | 397 +++++++++++++++++++++++++++++++++++++++++
> tests/generic/1230.out | 2 +
> 2 files changed, 399 insertions(+)
> create mode 100755 tests/generic/1230
> create mode 100644 tests/generic/1230.out
>
> diff --git a/tests/generic/1230 b/tests/generic/1230
> new file mode 100755
> index 00000000..cff5adc0
> --- /dev/null
> +++ b/tests/generic/1230
> @@ -0,0 +1,397 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
> +#
> +# FS QA Test No. 1230
> +#
> +# Test multi block atomic writes with sudden FS shutdowns to ensure
> +# the FS is not tearing the write operation
> +. ./common/preamble
> +. ./common/atomicwrites
> +_begin_fstest auto atomicwrites
> +
> +_require_scratch_write_atomic_multi_fsblock
> +_require_atomic_write_test_commands
> +_require_scratch_shutdown
> +_require_xfs_io_command "truncate"
is a similar fallocate test needed?
> +
> +_scratch_mkfs >> $seqres.full 2>&1
> +_scratch_mount >> $seqres.full
> +
> +testfile=$SCRATCH_MNT/testfile
> +touch $testfile
> +
> +awu_max=$(_get_atomic_write_unit_max $testfile)
> +blksz=$(_get_block_size $SCRATCH_MNT)
> +echo "Awu max: $awu_max" >> $seqres.full
> +
> +num_blocks=$((awu_max / blksz))
> +# keep initial value high for dry run. This will be
> +# tweaked in dry_run() based on device write speed.
> +filesize=$(( 10 * 1024 * 1024 * 1024 ))
could this cause some out-of-space issue? That's 10GB, right?
> +
> +_cleanup() {
> + [ -n "$awloop_pid" ] && kill $awloop_pid &> /dev/null
> + wait
> +}
> +
> +atomic_write_loop() {
> + local off=0
> + local size=$awu_max
> + for ((i=0; i<$((filesize / $size )); i++)); do
> + # Due to sudden shutdown this can produce errors so just
> + # redirect them to seqres.full
> + $XFS_IO_PROG -c "open -fsd $testfile" -c "pwrite -S 0x61 -DA -V1 -b $size $off $size" >> /dev/null 2>>$seqres.full
> + echo "Written to offset: $off" >> $tmp.aw
> + off=$((off + $size))
> + done
> +}
> +
> +# This test has the following flow:
> +# 1. Start doing sequential atomic writes in bg, upto $filesize
bg?
> +# 2. Sleep for 0.2s and shutdown the FS
> +# 3. kill the atomic write process
> +# 4. verify the writes were not torn
> +#
> +# We ideally want the shutdown to happen while an atomic write is ongoing
> +# but this gets tricky since faster devices can actually finish the whole
> +# atomic write loop before sleep 0.2s completes, resulting in the shutdown
> +# happening after the write loop which is not what we want. A simple solution
> +# to this is to increase $filesize so step 1 takes long enough but a big
> +# $filesize leads to create_mixed_mappings() taking very long, which is not
> +# ideal.
> +#
> +# Hence, use the dry_run function to figure out the rough device speed and set
> +# $filesize accordingly.
> +dry_run() {
> + echo >> $seqres.full
> + echo "# Estimating ideal filesize..." >> $seqres.full
> + atomic_write_loop &
> + awloop_pid=$!
> +
> + local i=0
> + # Wait for atleast first write to be recorded or 10s
> + while [ ! -f "$tmp.aw" -a $i -le 50 ]; do i=$((i + 1)); sleep 0.2; done
> +
> + if [[ $i -gt 50 ]]
> + then
> + _fail "atomic write process took too long to start"
> + fi
> +
> + echo >> $seqres.full
> + echo "# Shutting down filesystem while write is running" >> $seqres.full
> + _scratch_shutdown
> +
> + kill $awloop_pid 2>/dev/null # the process might have finished already
> + wait $awloop_pid
> + unset $awloop_pid
> +
> + bytes_written=$(tail -n 1 $tmp.aw | cut -d" " -f4)
> + echo "# Bytes written in 0.2s: $bytes_written" >> $seqres.full
> +
> + filesize=$((bytes_written * 3))
> + echo "# Setting \$filesize=$filesize" >> $seqres.full
> +
> + rm $tmp.aw
> + sleep 0.5
> +
> + _scratch_cycle_mount
> +
> +}
> +
> +create_mixed_mappings() {
Is this same as patch 08/12?
> + local file=$1
> + local size_bytes=$2
> +
> + echo "# Filling file $file with alternate mappings till size $size_bytes" >> $seqres.full
> + #Fill the file with alternate written and unwritten blocks
> + local off=0
> + local operations=("W" "U")
> +
> + for ((i=0; i<$((size_bytes / blksz )); i++)); do
> + index=$(($i % ${#operations[@]}))
> + map="${operations[$index]}"
> +
> + case "$map" in
> + "W")
> + $XFS_IO_PROG -fc "pwrite -b $blksz $off $blksz" $file >> /dev/null
does this just write random data? I don't see any pattern being set.
> + ;;
> + "U")
> + $XFS_IO_PROG -fc "falloc $off $blksz" $file >> /dev/null
> + ;;
> + esac
> + off=$((off + blksz))
> + done
> +
> + sync $file
> +}
> +
> +populate_expected_data() {
> + # create a dummy file with expected old data for different cases
> + create_mixed_mappings $testfile.exp_old_mixed $awu_max
> + expected_data_old_mixed=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_old_mixed)
> +
> + $XFS_IO_PROG -fc "falloc 0 $awu_max" $testfile.exp_old_zeroes >> $seqres.full
> + expected_data_old_zeroes=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_old_zeroes)
> +
> + $XFS_IO_PROG -fc "pwrite -b $awu_max 0 $awu_max" $testfile.exp_old_mapped >> $seqres.full
> + expected_data_old_mapped=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_old_mapped)
> +
> + # create a dummy file with expected new data
> + $XFS_IO_PROG -fc "pwrite -S 0x61 -b $awu_max 0 $awu_max" $testfile.exp_new >> $seqres.full
> + expected_data_new=$(od -An -t x1 -j 0 -N $awu_max $testfile.exp_new)
> +}
> +
> +verify_data_blocks() {
> + local verify_start=$1
> + local verify_end=$2
> + local expected_data_old="$3"
> + local expected_data_new="$4"
> +
> + echo >> $seqres.full
> + echo "# Checking data integrity from $verify_start to $verify_end" >> $seqres.full
> +
> + # After an atomic write, for every chunk we ensure that the underlying
> + # data is either the old data or new data as writes shouldn't get torn.
> + local off=$verify_start
> + while [[ "$off" -lt "$verify_end" ]]
> + do
> + #actual_data=$(xxd -s $off -l $awu_max -p $testfile)
> + actual_data=$(od -An -t x1 -j $off -N $awu_max $testfile)
> + if [[ "$actual_data" != "$expected_data_new" ]] && [[ "$actual_data" != "$expected_data_old" ]]
> + then
> + echo "Checksum match failed at off: $off size: $awu_max"
> + echo "Expected contents: (Either of the 2 below):"
> + echo
> + echo "Expected old: "
> + echo "$expected_data_old"
it would be nice if this was deterministic - see comment in
create_mixed_mappings
> + echo
> + echo "Expected new: "
> + echo "$expected_data_new"
nit: I am not sure what is meant by "expected". I would just have "new
data". We don't know what to expect, as it could be old or new, right?
> + echo
> + echo "Actual contents: "
> + echo "$actual_data"
> +
> + _fail
> + fi
> + echo -n "Check at offset $off suceeded! " >> $seqres.full
> + if [[ "$actual_data" == "$expected_data_new" ]]
> + then
> + echo "matched new" >> $seqres.full
> + elif [[ "$actual_data" == "$expected_data_old" ]]
> + then
> + echo "matched old" >> $seqres.full
> + fi
> + off=$(( off + awu_max ))
> + done
> +}
> +
> +# test data integrity for file by shutting down in between atomic writes
> +test_data_integrity() {
> + echo >> $seqres.full
> + echo "# Writing atomically to file in background" >> $seqres.full
> + atomic_write_loop &
> + awloop_pid=$!
> +
from here ...
> + local i=0
> + # Wait for atleast first write to be recorded or 10s
> + while [ ! -f "$tmp.aw" -a $i -le 50 ]; do i=$((i + 1)); sleep 0.2; done
> +
> + if [[ $i -gt 50 ]]
> + then
> + _fail "atomic write process took too long to start"
> + fi
> +
> + echo >> $seqres.full
> + echo "# Shutting down filesystem while write is running" >> $seqres.full
> + _scratch_shutdown
> +
> + kill $awloop_pid 2>/dev/null # the process might have finished already
> + wait $awloop_pid
> + unset $awloop_pid
... to here looks similar in many functions. Can we factor it out?
> +
> + last_offset=$(tail -n 1 $tmp.aw | cut -d" " -f4)
> + if [[ -z $last_offset ]]
> + then
> + last_offset=0
> + fi
> +
> + echo >> $seqres.full
> + echo "# Last offset of atomic write: $last_offset" >> $seqres.full
> +
> + rm $tmp.aw
> + sleep 0.5
> +
> + _scratch_cycle_mount
> +
> + # we want to verify all blocks around which the shutdown happended
> + verify_start=$(( last_offset - (awu_max * 5)))
> + if [[ $verify_start < 0 ]]
> + then
> + verify_start=0
> + fi
> +
> + verify_end=$(( last_offset + (awu_max * 5)))
> + if [[ "$verify_end" -gt "$filesize" ]]
> + then
> + verify_end=$filesize
> + fi
> +}
> +
> +# test data integrity for file wiht written and unwritten mappings
with
> +test_data_integrity_mixed() {
> + $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
> +
> + echo >> $seqres.full
> + echo "# Creating testfile with mixed mappings" >> $seqres.full
> + create_mixed_mappings $testfile $filesize
> +
> + test_data_integrity
> +
> + verify_data_blocks $verify_start $verify_end "$expected_data_old_mixed" "$expected_data_new"
> +}
> +
> +# test data integrity for file with completely written mappings
> +test_data_integrity_writ() {
please spell "writ" out fully, which I think should be "written"
> + $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
> +
> + echo >> $seqres.full
> + echo "# Creating testfile with fully written mapping" >> $seqres.full
> + $XFS_IO_PROG -c "pwrite -b $filesize 0 $filesize" $testfile >> $seqres.full
> + sync $testfile
> +
> + test_data_integrity
> +
> + verify_data_blocks $verify_start $verify_end "$expected_data_old_mapped" "$expected_data_new"
> +}
> +
> +# test data integrity for file with completely unwritten mappings
> +test_data_integrity_unwrit() {
same as above
> + $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
> +
> + echo >> $seqres.full
> + echo "# Creating testfile with fully unwritten mappings" >> $seqres.full
> + $XFS_IO_PROG -c "falloc 0 $filesize" $testfile >> $seqres.full
> + sync $testfile
> +
> + test_data_integrity
> +
> + verify_data_blocks $verify_start $verify_end "$expected_data_old_zeroes" "$expected_data_new"
> +}
> +
> +# test data integrity for file with no mappings
> +test_data_integrity_hole() {
> + $XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
> +
> + echo >> $seqres.full
> + echo "# Creating testfile with no mappings" >> $seqres.full
> + $XFS_IO_PROG -c "truncate $filesize" $testfile >> $seqres.full
> + sync $testfile
> +
> + test_data_integrity
> +
> + verify_data_blocks $verify_start $verify_end "$expected_data_old_zeroes" "$expected_data_new"
> +}
> +
> +test_filesize_integrity() {
> + $XFS_IO_PROG -c "truncate 0" $testfile >> $seqres.full
> +
> + echo >> $seqres.full
> + echo "# Performing extending atomic writes over file in background" >> $seqres.full
> + atomic_write_loop &
> + awloop_pid=$!
> +
> + local i=0
> + # Wait for atleast first write to be recorded or 10s
> + while [ ! -f "$tmp.aw" -a $i -le 50 ]; do i=$((i + 1)); sleep 0.2; done
> +
> + if [[ $i -gt 50 ]]
> + then
> + _fail "atomic write process took too long to start"
> + fi
> +
> + echo >> $seqres.full
> + echo "# Shutting down filesystem while write is running" >> $seqres.full
> + _scratch_shutdown
> +
> + kill $awloop_pid 2>/dev/null # the process might have finished already
> + wait $awloop_pid
> + unset $awloop_pid
> +
> + local last_offset=$(tail -n 1 $tmp.aw | cut -d" " -f4)
> + if [[ -z $last_offset ]]
> + then
> + last_offset=0
> + fi
> +
> + echo >> $seqres.full
> + echo "# Last offset of atomic write: $last_offset" >> $seqres.full
> + rm $tmp.aw
> + sleep 0.5
> +
> + _scratch_cycle_mount
> + local filesize=$(_get_filesize $testfile)
> + echo >> $seqres.full
> + echo "# Filesize after shutdown: $filesize" >> $seqres.full
> +
> + # To confirm that the write went atomically, we check:
> + # 1. The last block should be a multiple of awu_max
> + # 2. The last block should be the completely new data
> +
> + if (( $filesize % $awu_max ))
> + then
> + echo "Filesize after shutdown ($filesize) not a multiple of atomic write unit ($awu_max)"
> + fi
> +
> + verify_start=$(( filesize - (awu_max * 5)))
> + if [[ $verify_start < 0 ]]
> + then
> + verify_start=0
> + fi
> +
> + local verify_end=$filesize
> +
> + # Here the blocks should always match new data hence, for simplicity of
> + # code, just corrupt the $expected_data_old buffer so it never matches
> + local expected_data_old="POISON"
> + verify_data_blocks $verify_start $verify_end "$expected_data_old" "$expected_data_new"
> +}
> +
> +$XFS_IO_PROG -fc "truncate 0" $testfile >> $seqres.full
> +
> +dry_run
> +
> +echo >> $seqres.full
> +echo "# Populating expected data buffers" >> $seqres.full
> +populate_expected_data
> +
> +# Loop 20 times to shake out any races due to shutdown
> +for ((iter=0; iter<20; iter++))
> +do
> + echo >> $seqres.full
> + echo "------ Iteration $iter ------" >> $seqres.full
> +
> + echo >> $seqres.full
> + echo "# Starting data integrity test for atomic writes over mixed mapping" >> $seqres.full
> + test_data_integrity_mixed
> +
> + echo >> $seqres.full
> + echo "# Starting data integrity test for atomic writes over fully written mapping" >> $seqres.full
> + test_data_integrity_writ
> +
> + echo >> $seqres.full
> + echo "# Starting data integrity test for atomic writes over fully unwritten mapping" >> $seqres.full
> + test_data_integrity_unwrit
> +
> + echo >> $seqres.full
> + echo "# Starting data integrity test for atomic writes over holes" >> $seqres.full
> + test_data_integrity_hole
> +
> + echo >> $seqres.full
> + echo "# Starting filesize integrity test for atomic writes" >> $seqres.full
what does "Starting filesize integrity test" mean?
> + test_filesize_integrity
> +done
> +
> +echo "Silence is golden"
> +status=0
> +exit
> diff --git a/tests/generic/1230.out b/tests/generic/1230.out
> new file mode 100644
> index 00000000..d01f54ea
> --- /dev/null
> +++ b/tests/generic/1230.out
> @@ -0,0 +1,2 @@
> +QA output created by 1230
> +Silence is golden
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc
2025-08-22 8:02 ` [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc Ojaswin Mujoo
2025-08-28 15:09 ` Darrick J. Wong
@ 2025-09-02 15:52 ` John Garry
1 sibling, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:52 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
>
> This test does a lot of parallel RWF_ATOMIC IO on a preallocated file to
> stress the write and end-io unwritten conversion code paths. We brute
> force this for different blocksize and clustersizes and after each
> iteration we ensure the data was not torn or corrupted using fio crc
> verification.
>
> Note that in this test we use overlapping atomic writes of same io size.
> Although serializing racing writes is not guaranteed for RWF_ATOMIC,
> NVMe and SCSI provide this guarantee as an inseparable feature to
> power-fail atomicity. Keeping the iosize as same also ensures that ext4
> doesn't tear the write due to racing ioend unwritten conversion.
>
> The value of this test is that we make sure the RWF_ATOMIC is handled
> correctly by ext4 as well as test that the block layer doesn't split or
> only generate multiple bios for an atomic write.
>
> Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v5 11/12] ext4: Test atomic writes allocation and write codepaths with bigalloc
2025-08-22 8:02 ` [PATCH v5 11/12] ext4: Test atomic writes allocation and write " Ojaswin Mujoo
@ 2025-09-02 15:54 ` John Garry
0 siblings, 0 replies; 31+ messages in thread
From: John Garry @ 2025-09-02 15:54 UTC (permalink / raw)
To: Ojaswin Mujoo, Zorro Lang, fstests
Cc: Ritesh Harjani, djwong, tytso, linux-xfs, linux-kernel,
linux-ext4
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
>
> This test does a parallel RWF_ATOMIC IO on a multiple truncated files in
> a small FS. The idea is to stress ext4 allocator to ensure we are able
> to handle low space scenarios correctly with atomic writes. We brute
> force this for different blocksize and clustersizes and after each
> iteration we ensure the data was not torn or corrupted using fio crc
> verification.
>
> Note that in this test we use overlapping atomic writes of same io size.
> Although serializing racing writes is not guaranteed for RWF_ATOMIC,
> NVMe and SCSI provide this guarantee as an inseparable feature to
> power-fail atomicity. Keeping the iosize as same also ensures that ext4
> doesn't tear the write due to racing ioend unwritten conversion.
>
> The value of this test is that we make sure the RWF_ATOMIC is handled
> correctly by ext4 as well as test that the block layer doesn't split or
> only generate multiple bios for an atomic write.
>
> Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> ---
> tests/ext4/062 | 203 +++++++++++++++++++++++++++++++++++++++++++++
> tests/ext4/062.out | 2 +
> 2 files changed, 205 insertions(+)
> create mode 100755 tests/ext4/062
> create mode 100644 tests/ext4/062.out
>
> diff --git a/tests/ext4/062 b/tests/ext4/062
> new file mode 100755
> index 00000000..d48f69d3
> --- /dev/null
> +++ b/tests/ext4/062
> @@ -0,0 +1,203 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
> +#
> +# FS QA Test 062
> +#
> +# This test does a parallel RWF_ATOMIC IO on a multiple truncated files in a
> +# small FS. The idea is to stress ext4 allocator to ensure we are able to
> +# handle low space scenarios correctly with atomic writes.. We brute force this
> +# for all possible blocksize and clustersizes and after each iteration we
> +# ensure the data was not torn or corrupted using fio crc verification.
> +#
> +# Note that in this test we use overlapping atomic writes of same io size.
> +# Although serializing racing writes is not guaranteed for RWF_ATOMIC, NVMe and
> +# SCSI provide this guarantee as an inseparable feature to power-fail
> +# atomicity. Keeping the iosize as same also ensures that ext4 doesn't tear the
> +# write due to racing ioend unwritten conversion.
> +#
> +# The value of this test is that we make sure the RWF_ATOMIC is handled
> +# correctly by ext4 as well as test that the block layer doesn't split or only
> +# generate multiple bios for an atomic write.
> +#
> +
> +. ./common/preamble
> +. ./common/atomicwrites
> +
> +_begin_fstest auto rw stress atomicwrites
> +
> +_require_scratch_write_atomic
> +_require_aiodio
> +_require_fio_version "3.38+"
> +
> +FSSIZE=$((360*1024*1024))
> +FIO_LOAD=$(($(nproc) * LOAD_FACTOR))
> +
> +# Calculate bs as per bdev atomic write units.
> +bdev_awu_min=$(_get_atomic_write_unit_min $SCRATCH_DEV)
> +bdev_awu_max=$(_get_atomic_write_unit_max $SCRATCH_DEV)
> +bs=$(_max 4096 "$bdev_awu_min")
> +
> +function create_fio_configs()
> +{
> + local bsize=$1
> + create_fio_aw_config $bsize
> + create_fio_verify_config $bsize
> +}
> +
> +function create_fio_verify_config()
> +{
> + local bsize=$1
> +cat >$fio_verify_config <<EOF
> + [global]
> + direct=1
> + ioengine=libaio
> + rw=read
> + bs=$bsize
> + fallocate=truncate
> + size=$((FSSIZE / 12))
> + iodepth=$FIO_LOAD
> + numjobs=$FIO_LOAD
> + group_reporting=1
> + atomic=1
> +
> + verify_only=1
> + verify_state_save=0
> + verify=crc32c
> + verify_fatal=1
> + verify_write_sequence=0
> +
> + [verify-job1]
> + filename=$SCRATCH_MNT/testfile-job1
> +
> + [verify-job2]
> + filename=$SCRATCH_MNT/testfile-job2
> +
> + [verify-job3]
> + filename=$SCRATCH_MNT/testfile-job3
> +
> + [verify-job4]
> + filename=$SCRATCH_MNT/testfile-job4
> +
> + [verify-job5]
> + filename=$SCRATCH_MNT/testfile-job5
> +
> + [verify-job6]
> + filename=$SCRATCH_MNT/testfile-job6
> +
> + [verify-job7]
> + filename=$SCRATCH_MNT/testfile-job7
> +
> + [verify-job8]
> + filename=$SCRATCH_MNT/testfile-job8
do you really need multiple jobs for verify?
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2025-09-02 15:55 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-22 8:01 [PATCH v5 00/11] Add more tests for multi fs block atomic writes Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 01/12] common/rc: Add _min() and _max() helpers Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 02/12] common/rc: Add _require_fio_version helper Ojaswin Mujoo
2025-08-25 16:08 ` Zorro Lang
2025-08-27 15:16 ` Ojaswin Mujoo
2025-08-28 15:09 ` Darrick J. Wong
2025-08-29 16:59 ` Ojaswin Mujoo
2025-08-30 17:09 ` Zorro Lang
2025-09-01 11:40 ` Ojaswin Mujoo
2025-09-02 5:30 ` Zorro Lang
2025-09-02 8:29 ` John Garry
2025-09-02 14:50 ` John Garry
2025-08-22 8:02 ` [PATCH v5 03/12] common/rc: Add a helper to run fsx on a given file Ojaswin Mujoo
2025-08-22 8:02 ` [PATCH v5 04/12] ltp/fsx.c: Add atomic writes support to fsx Ojaswin Mujoo
2025-09-02 15:06 ` John Garry
2025-08-22 8:02 ` [PATCH v5 05/12] generic: Add atomic write test using fio crc check verifier Ojaswin Mujoo
2025-09-02 15:09 ` John Garry
2025-08-22 8:02 ` [PATCH v5 06/12] generic: Add atomic write test using fio verify on file mixed mappings Ojaswin Mujoo
2025-09-02 15:10 ` John Garry
2025-08-22 8:02 ` [PATCH v5 07/12] generic: Add atomic write multi-fsblock O_[D]SYNC tests Ojaswin Mujoo
2025-09-02 15:14 ` John Garry
2025-08-22 8:02 ` [PATCH v5 08/12] generic: Stress fsx with atomic writes enabled Ojaswin Mujoo
2025-09-02 15:18 ` John Garry
2025-08-22 8:02 ` [PATCH v5 09/12] generic: Add sudden shutdown tests for multi block atomic writes Ojaswin Mujoo
2025-09-02 15:49 ` John Garry
2025-08-22 8:02 ` [PATCH v5 10/12] ext4: test atomic write and ioend codepaths with bigalloc Ojaswin Mujoo
2025-08-28 15:09 ` Darrick J. Wong
2025-09-02 15:52 ` John Garry
2025-08-22 8:02 ` [PATCH v5 11/12] ext4: Test atomic writes allocation and write " Ojaswin Mujoo
2025-09-02 15:54 ` John Garry
2025-08-22 8:02 ` [PATCH v5 12/12] ext4: Atomic write test for extent split across leaf nodes Ojaswin Mujoo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).