Add more tests for multi fs block atomic writes
v7 cover Add more tests for multi fs block atomic writes
Changes in v7:
- Picked up reviews from John! (Thanks)
- [9/11] Change "integrity test.." -> "torn write test.." for better clarity
Changes in v6:[5]
- Picked up reviews from Darrick, Zorro and John! (Thanks)
- Added _require_fio_atomic_writes helper in patch 3 as a wrapper arounc __require_fio_version
- minor spelling and refactors
[5] https://lore.kernel.org/fstests/[email protected]/
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/[email protected]/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/[email protected]/
[3] https://lore.kernel.org/fstests/[email protected]/
Changes in v2 [1]:
- (1/13) new patch with _min and _max helpers
- (2/13) remove setup_fs_options and add fsx specific 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/[email protected]/
* 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/[email protected]/
Ojaswin Mujoo (10):
common/rc: Add _min() and _max() helpers
common/rc: Add fio atomic write helpers
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 | 88 +++++++++- 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 | 138 ++++++++++++++++ tests/generic/1228.out | 2 + tests/generic/1229 | 68 ++++++++ tests/generic/1229.out | 2 + tests/generic/1230 | 368 +++++++++++++++++++++++++++++++++++++++++ tests/generic/1230.out | 2 + 18 files changed, 1512 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
Signature · 1 line
2.49.0
PATCH v7 1/12 common/rc: Add _min() and _max() helpers
Many programs open code these functionalities so add it as a generic helper in common/rc
--- common/rc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
common/rc +22 -0
@@ -5978,6 +5978,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
Signature · 1 line
2.49.0
PATCH v7 2/12 common/rc: Add fio atomic write helpers
In reply to Ojaswin MujooThe 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 internal helper __require_fio_version to require specific versions of fio to work past such issues. Further, add the high level _require_fio_atomic_writes helper which tests can use to ensure fio has the right version for atomic writes.
--- common/rc | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
common/rc +43 -0
@@ -6000,6 +6000,49 @@ _max() { echo $ret } +# Due to reasons explained in fio commit 40f1fc11d, fio version between +# v3.33 and v3.38 have atomic= feature but it is a no-op and doesn't do +# RWF_ATOMIC write. Hence, use this helper to ensure fio has the +# required support. Currently, the simplest way we have is to ensure +# the version. +_require_fio_atomic_writes() { + __require_fio_version "3.38+" +} + +# 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) +# +# Internal helper, avoid using directly in tests. +__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
Signature · 1 line
2.49.0
Re: [PATCH v7 02/12] common/rc: Add fio atomic write helpers
Quoted 8 lines · Ojaswin Mujoo
The main motivation of adding this function on top of
_require_fiois 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_fiow/ atomic=1 will succeed even though it would not be doing atomic writes.
Quoted 8 lines
Hence, add an internal helper
__require_fio_versionto require specific versions of fio to work past such issues. Further, add the high level_require_fio_atomic_writeshelper which tests can use to ensure fio has the right version for atomic writes.Reviewed-by Zorro Lang <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>
Looks ok to me,
--D
Quoted 61 lines
--- common/rc | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)common/rc +43
-0@@ -6000,6 +6000,49 @@ _max() { echo $ret } +# Due to reasons explained in fio commit 40f1fc11d, fio version between +# v3.33 and v3.38 have atomic= feature but it is a no-op and doesn't do +# RWF_ATOMIC write. Hence, use this helper to ensure fio has the +# required support. Currently, the simplest way we have is to ensure +# the version. +_require_fio_atomic_writes() { + __require_fio_version "3.38+" +} + +# 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) +# +# Internal helper, avoid using directly in tests. +__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/trueSignature · 1 line
2.49.0
PATCH v7 3/12 common/rc: Add a helper to run fsx on a given file
In reply to Ojaswin MujooCurrently 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.
--- common/rc | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-)
common/rc +20 -3
@@ -5203,13 +5203,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
@@ -5221,6 +5232,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() {
Signature · 1 line
2.49.0
PATCH v7 4/12 ltp/fsx.c: Add atomic writes support to fsx
In reply to Ojaswin MujooImplement atomic write support to help fuzz atomic writes with fsx.
--- ltp/fsx.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 5 deletions(-)
ltp/fsx.c +110 -5
@@ -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 between 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 @@ voidusage (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 ())
Signature · 1 line
2.49.0
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 149 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>--- ltp/fsx.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 5 deletions(-)ltp/fsx.c +60
-3@@ -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 between 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 )
ltp/fsx.c do_dedupe_range(unsigned offset, unsigned length, unsigned dest) · line 1851
Quoted 10 lines
ltp/fsx.c +7
-0@@ -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" );
This patch causes some fsx test cases fail (e.g. g/127, g/231) as:
127.out.bad renamed from dev/fd/63 +6 -0
@@ -1,7 +1,13 @@ QA output created by 127 +main: atomic writes need O_DIRECT (-Z), disabling! All 100000 operations completed A-OK! +main: atomic writes need O_DIRECT (-Z), disabling! All 100000 operations completed A-OK! +main: atomic writes need O_DIRECT (-Z), disabling! All 100000 operations completed A-OK! +main: atomic writes need O_DIRECT (-Z), disabling! All 100000 operations completed A-OK! +main: atomic writes need O_DIRECT (-Z), disabling! All 100000 operations completed A-OK! +main: atomic writes need O_DIRECT (-Z), disabling! All 100000 operations completed A-OK!
Due to you didn't deal with the "-q" (quiet) option at here. If you don't have objection, I'll change this part as:
if (o_direct != O_DIRECT && !quiet)
And ...
Quoted 10 lines
ltp/fsx.c +10
-0+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 ; + }
ltp/fsx.c do_dedupe_range(unsigned offset, unsigned length, unsigned dest) · line 1871
Quoted 10 lines
ltp/fsx.c +10
-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" );
... change this line as:
if (!quiet)
fprintf(stderr, "main: IO Stack does not support " "atomic writes, disabling!\n");
When I merge it.
Thanks,
Zorro
Quoted 74 lines
ltp/fsx.c +23
-2+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 @@ voidusage (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 ())Signature · 1 line
2.49.0
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
In reply to Ojaswin MujooQuoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Thanks,
Zorro
Quoted 245 lines
ltp/fsx.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 5 deletions(-)ltp/fsx.c +110
-5@@ -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 between 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 @@ voidusage (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 ())Signature · 1 line
2.49.0
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.
Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Regards,
ojaswin
Quoted 249 lines
Thanks,
ZorroQuoted 245 lines
ltp/fsx.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 5 deletions(-)ltp/fsx.c +110
-5@@ -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 between 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 @@ voidusage (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 ())Signature · 1 line
2.49.0
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
ltp/fsx.c +6 -4
@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }
@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }
But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro
[1]
263.out.bad renamed from dev/fd/63 +335 -1
338 lines not on this page · raw message
Thanks,
Zorro
Quoted 254 lines
Regards,
ojaswinQuoted 249 lines
Thanks,
ZorroQuoted 245 lines
ltp/fsx.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 5 deletions(-)ltp/fsx.c +110
-5@@ -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 between 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 @@ voidusage (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 ())Signature · 1 line
2.49.0
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro
<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")
Quoted 2 lines
Thanks,
Zorro
Hi Zorro, just to confirm is this on an older kernel that doesnt support RWF_ATOMIC or on a kernle that does support it.
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 135 lines · Ojaswin Mujoo
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")
Quoted 5 lines
Quoted 2 lines
Thanks,
ZorroHi Zorro, just to confirm is this on an older kernel that doesnt support
RWF_ATOMICor on a kernle that does support it.
I tested on linux 6.16 and current latest linux v6.17+ (will be 6.18-rc1 later). About the RWF_ATOMIC flag in my system:
# grep -rsn RWF_ATOMIC /usr/include/ /usr/include/bits/uio-ext.h:51:#define RWF_ATOMIC 0x00000040 /* Write is to be issued with torn-write /usr/include/linux/fs.h:424:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040) /usr/include/linux/fs.h:431: RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ /usr/include/xfs/linux.h:236:#ifndef RWF_ATOMIC /usr/include/xfs/linux.h:237:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040)
Thanks,
Zorro
Quoted 4 lines
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 145 lines · Zorro Lang
Quoted 135 lines · Ojaswin Mujoo
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")Quoted 5 lines
Quoted 2 lines
Thanks,
ZorroHi Zorro, just to confirm is this on an older kernel that doesnt support
RWF_ATOMICor on a kernle that does support it.I tested on linux 6.16 and current latest linux v6.17+ (will be 6.18-rc1 later). About the
RWF_ATOMICflag in my system:
Quoted 6 lines
# grep -rsn RWF_ATOMIC /usr/include/ /usr/include/bits/uio-ext.h:51:#define RWF_ATOMIC 0x00000040 /* Write is to be issued with torn-write /usr/include/linux/fs.h:424:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040) /usr/include/linux/fs.h:431: RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ /usr/include/xfs/linux.h:236:#ifndef RWF_ATOMIC /usr/include/xfs/linux.h:237:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040)
Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205
We see that 0x0000 was expected but we got 0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:
+205(205 mod 256): ZERO 0x6dbe6 thru 0x6e6aa (0xac5 bytes)
This doesn't even overlap the range that is bad. (0x1c000 to 0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is 0xcdcd which is something xfs_io -c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).
I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
I'm not sure how this will affect the upcoming release, if you want shall I send a small patch to make the atomic writes feature default off instead of default on till we root cause this?
Regards,
Ojaswin
Quoted 9 lines
Thanks,
ZorroQuoted 4 lines
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 179 lines · Ojaswin Mujoo
Quoted 145 lines · Zorro Lang
Quoted 135 lines · Ojaswin Mujoo
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")Quoted 5 lines
Quoted 2 lines
Thanks,
ZorroHi Zorro, just to confirm is this on an older kernel that doesnt support
RWF_ATOMICor on a kernle that does support it.I tested on linux 6.16 and current latest linux v6.17+ (will be 6.18-rc1 later). About the
RWF_ATOMICflag in my system:Quoted 6 lines
# grep -rsn RWF_ATOMIC /usr/include/ /usr/include/bits/uio-ext.h:51:#define RWF_ATOMIC 0x00000040 /* Write is to be issued with torn-write /usr/include/linux/fs.h:424:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040) /usr/include/linux/fs.h:431: RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ /usr/include/xfs/linux.h:236:#ifndef RWF_ATOMIC /usr/include/xfs/linux.h:237:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040)Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Quoted 6 lines
I'm not sure how this will affect the upcoming release, if you want shall I send a small patch to make the atomic writes feature default off instead of default on till we root cause this?
Regards,
Ojaswin
Hi Zorro,
So I'm able to narrow down the opoerations and replicate it via the following replay file:
# -----
# replay.fsxops # ----- write_atomic 0x57000 0x1000 0x69690 write_atomic 0x66000 0x1000 0x4de00 write_atomic 0x18000 0x1000 0x2c800 copy_range 0x20000 0x1000 0xe00 0x70e00 write_atomic 0x18000 0x1000 0x70e00 copy_range 0x21000 0x1000 0x23000 0x74218 truncate 0x0 0x11200 0x4daef * write_atomic 0x43000 0x1000 0x11200 * write_atomic 0x15000 0x1000 0x44000 copy_range 0xd000 0x1000 0x1d800 0x44000 mapread 0x1c000 0x1803 0x1e400 *
Command: ./ltp/fsx -N 10000 -o 8192 -l 500000 -r 4096 -t 512 -w 512 -Z -FKuHzI --replay-ops replay.fsxops $MNT/junk
$MNT/junk is always opened O_TRUNC and is an on an XFS FS where the disk is non-atomic so all RWF_ATOMIC writes are software emulated.
Here are the logs generated for this run:
Seed set to 1
main: filesystem does not support exchange range, disabling!
READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/test/junk
OFFSET GOOD BAD RANGE 0x1d000 0x0000 0xf322 0x0
operation# (mod 256) for the bad data may be 2430x1d001 0x0000 0x22f3 0x1
operation# (mod 256) for the bad data may be 2430x1d002 0x0000 0xf391 0x2
operation# (mod 256) for the bad data may be 2430x1d003 0x0000 0x91f3 0x3
<... a few more such lines ..>
LOG DUMP (11 total operations):openat(AT_FDCWD, "/mnt/test/junk.fsxops", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 7
1( 1 mod 256): WRITE 0x57000 thru 0x57fff (0x1000 bytes) HOLE ***WWWW ATOMIC 2( 2 mod 256): WRITE 0x66000 thru 0x66fff (0x1000 bytes) HOLE ATOMIC 3( 3 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 4( 4 mod 256): COPY 0x20000 thru 0x20fff (0x1000 bytes) to 0xe00 thru 0x1dff 5( 5 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 6( 6 mod 256): COPY 0x21000 thru 0x21fff (0x1000 bytes) to 0x23000 thru 0x23fff 7( 7 mod 256): TRUNCATE DOWN from 0x67000 to 0x11200 ******WWWW 8( 8 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW ATOMIC 9( 9 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) ATOMIC
10( 10 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff
11( 11 mod 256): MAPREAD 0x1c000 thru 0x1d802 (0x1803 bytes) ***RRRR*** Log of operations saved to "/mnt/test/junk.fsxops"; replay with --replay-ops Correct content saved for comparison
(maybe hexdump "/mnt/test/junk" vs "/mnt/test/junk.fsxgood")
+++ exited with 110 +++
We can see that the bad data is detected in the final MAPREAD operation and and bad offset is at 0x1d000. If we look at the operations dump above its clear that none of the operations should be modifying the 0x1d000 so we should have been reading 0s but yet we see some junk data there in the file:
$ hexdump /mnt/test/junk -s 0x1c000 -n0x1020 001c000 0000 0000 0000 0000 0000 0000 0000 0000 * 001d000 22f3 91f3 7ff3 3af3 39f3 23f3 6df3 c2f3 001d010 c5f3 f6f3 a6f3 1ef3 58f3 40f3 32f3 5ff3 001d020
Another thing to not is that I can't reproduce the above on scsi-debug device. @Darrick, @John, could this be an issue in kernel?
Regards,
ojaswin
Quoted 10 lines
Quoted 10 lines
Thanks,
ZorroQuoted 4 lines
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 260 lines · Ojaswin Mujoo
Quoted 179 lines · Ojaswin Mujoo
Quoted 145 lines · Zorro Lang
Quoted 135 lines · Ojaswin Mujoo
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")Quoted 5 lines
Quoted 2 lines
Thanks,
ZorroHi Zorro, just to confirm is this on an older kernel that doesnt support
RWF_ATOMICor on a kernle that does support it.I tested on linux 6.16 and current latest linux v6.17+ (will be 6.18-rc1 later). About the
RWF_ATOMICflag in my system:Quoted 6 lines
# grep -rsn RWF_ATOMIC /usr/include/ /usr/include/bits/uio-ext.h:51:#define RWF_ATOMIC 0x00000040 /* Write is to be issued with torn-write /usr/include/linux/fs.h:424:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040) /usr/include/linux/fs.h:431: RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ /usr/include/xfs/linux.h:236:#ifndef RWF_ATOMIC /usr/include/xfs/linux.h:237:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040)Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Quoted 6 lines
I'm not sure how this will affect the upcoming release, if you want shall I send a small patch to make the atomic writes feature default off instead of default on till we root cause this?
Regards,
OjaswinHi Zorro,
So I'm able to narrow down the opoerations and replicate it via the following replay file:
# -----
# replay.fsxops # ----- write_atomic 0x57000 0x1000 0x69690 write_atomic 0x66000 0x1000 0x4de00 write_atomic 0x18000 0x1000 0x2c800 copy_range 0x20000 0x1000 0xe00 0x70e00 write_atomic 0x18000 0x1000 0x70e00 copy_range 0x21000 0x1000 0x23000 0x74218 truncate 0x0 0x11200 0x4daef * write_atomic 0x43000 0x1000 0x11200 * write_atomic 0x15000 0x1000 0x44000 copy_range 0xd000 0x1000 0x1d800 0x44000 mapread 0x1c000 0x1803 0x1e400 *Command: ./ltp/fsx -N 10000 -o 8192 -l 500000 -r 4096 -t 512 -w 512 -Z -FKuHzI --replay-ops replay.fsxops $MNT/junk
$MNT/junk is always opened
O_TRUNCand is an on an XFS FS where the disk is non-atomic so allRWF_ATOMICwrites are software emulated.Here are the logs generated for this run:
Seed set to 1
main: filesystem does not support exchange range, disabling!READ BAD DATA: offset =
0x1c000, size =0x1803, fname =/mnt/test/junkOFFSET GOOD BAD RANGE 0x1d000 0x0000 0xf322 0x0operation# (mod 256) for the bad data may be 243
0x1d0010x00000x22f30x1
operation# (mod 256) for the bad data may be 2430x1d0020x00000xf3910x2
operation# (mod 256) for the bad data may be 2430x1d0030x00000x91f30x3
<... a few more such lines ..>LOG DUMP (11 total operations):
openat(AT_FDCWD, "/mnt/test/junk.fsxops", O_WRONLY|O_CREAT|O_TRUNC, 0666)= 71( 1 mod 256): WRITE 0x57000 thru 0x57fff (0x1000 bytes) HOLE ***WWWW ATOMIC 2( 2 mod 256): WRITE 0x66000 thru 0x66fff (0x1000 bytes) HOLE ATOMIC 3( 3 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 4( 4 mod 256): COPY 0x20000 thru 0x20fff (0x1000 bytes) to 0xe00 thru 0x1dff 5( 5 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 6( 6 mod 256): COPY 0x21000 thru 0x21fff (0x1000 bytes) to 0x23000 thru 0x23fff 7( 7 mod 256): TRUNCATE DOWN from 0x67000 to 0x11200 ******WWWW 8( 8 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW ATOMIC 9( 9 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) ATOMIC10( 10 mod 256): COPY
0xd000thru0xdfff(0x1000bytes) to0x1d800thru0x1e7ff
11( 11 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR*** Log of operations saved to "/mnt/test/junk.fsxops"; replay with --replay-ops Correct content saved for comparison
(maybe hexdump "/mnt/test/junk" vs "/mnt/test/junk.fsxgood")
+++ exited with 110 +++We can see that the bad data is detected in the final MAPREAD operation and and bad offset is at
0x1d000. If we look at the operations dump above its clear that none of the operations should be modifying the0x1d000so we should have been reading 0s but yet we see some junk data there in the file:$ hexdump /mnt/test/junk -s 0x1c000 -n0x1020 001c000 0000 0000 0000 0000 0000 0000 0000 0000 * 001d000 22f3 91f3 7ff3 3af3 39f3 23f3 6df3 c2f3 001d010 c5f3 f6f3 a6f3 1ef3 58f3 40f3 32f3 5ff3 001d020
Quoted 2 lines
Another thing to not is that I can't reproduce the above on scsi-debug device. @Darrick, @John, could this be an issue in kernel?
Hi Ojaswin,
If we can be sure this's a kernel bug, rather than a fstests (patch) issue, I think we can merge this patchset to expose this bug. Does this make sense to you and others?
Thanks,
Zorro
Quoted 14 lines
Regards,
ojaswinQuoted 10 lines
Quoted 10 lines
Thanks,
ZorroQuoted 4 lines
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 261 lines · Zorro Lang
Quoted 260 lines · Ojaswin Mujoo
Quoted 179 lines · Ojaswin Mujoo
Quoted 145 lines · Zorro Lang
Quoted 135 lines · Ojaswin Mujoo
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")Quoted 5 lines
Quoted 2 lines
Thanks,
ZorroHi Zorro, just to confirm is this on an older kernel that doesnt support
RWF_ATOMICor on a kernle that does support it.I tested on linux 6.16 and current latest linux v6.17+ (will be 6.18-rc1 later). About the
RWF_ATOMICflag in my system:Quoted 6 lines
# grep -rsn RWF_ATOMIC /usr/include/ /usr/include/bits/uio-ext.h:51:#define RWF_ATOMIC 0x00000040 /* Write is to be issued with torn-write /usr/include/linux/fs.h:424:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040) /usr/include/linux/fs.h:431: RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ /usr/include/xfs/linux.h:236:#ifndef RWF_ATOMIC /usr/include/xfs/linux.h:237:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040)Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Quoted 6 lines
I'm not sure how this will affect the upcoming release, if you want shall I send a small patch to make the atomic writes feature default off instead of default on till we root cause this?
Regards,
OjaswinHi Zorro,
So I'm able to narrow down the opoerations and replicate it via the following replay file:
# -----
# replay.fsxops # ----- write_atomic 0x57000 0x1000 0x69690 write_atomic 0x66000 0x1000 0x4de00 write_atomic 0x18000 0x1000 0x2c800 copy_range 0x20000 0x1000 0xe00 0x70e00 write_atomic 0x18000 0x1000 0x70e00 copy_range 0x21000 0x1000 0x23000 0x74218 truncate 0x0 0x11200 0x4daef * write_atomic 0x43000 0x1000 0x11200 * write_atomic 0x15000 0x1000 0x44000 copy_range 0xd000 0x1000 0x1d800 0x44000 mapread 0x1c000 0x1803 0x1e400 *Command: ./ltp/fsx -N 10000 -o 8192 -l 500000 -r 4096 -t 512 -w 512 -Z -FKuHzI --replay-ops replay.fsxops $MNT/junk
$MNT/junk is always opened
O_TRUNCand is an on an XFS FS where the disk is non-atomic so allRWF_ATOMICwrites are software emulated.Here are the logs generated for this run:
Seed set to 1
main: filesystem does not support exchange range, disabling!READ BAD DATA: offset =
0x1c000, size =0x1803, fname =/mnt/test/junkOFFSET GOOD BAD RANGE 0x1d000 0x0000 0xf322 0x0operation# (mod 256) for the bad data may be 243
0x1d0010x00000x22f30x1
operation# (mod 256) for the bad data may be 2430x1d0020x00000xf3910x2
operation# (mod 256) for the bad data may be 2430x1d0030x00000x91f30x3
<... a few more such lines ..>LOG DUMP (11 total operations):
openat(AT_FDCWD, "/mnt/test/junk.fsxops", O_WRONLY|O_CREAT|O_TRUNC, 0666)= 71( 1 mod 256): WRITE 0x57000 thru 0x57fff (0x1000 bytes) HOLE ***WWWW ATOMIC 2( 2 mod 256): WRITE 0x66000 thru 0x66fff (0x1000 bytes) HOLE ATOMIC 3( 3 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 4( 4 mod 256): COPY 0x20000 thru 0x20fff (0x1000 bytes) to 0xe00 thru 0x1dff 5( 5 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 6( 6 mod 256): COPY 0x21000 thru 0x21fff (0x1000 bytes) to 0x23000 thru 0x23fff 7( 7 mod 256): TRUNCATE DOWN from 0x67000 to 0x11200 ******WWWW 8( 8 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW ATOMIC 9( 9 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) ATOMIC10( 10 mod 256): COPY
0xd000thru0xdfff(0x1000bytes) to0x1d800thru0x1e7ff
11( 11 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR*** Log of operations saved to "/mnt/test/junk.fsxops"; replay with --replay-ops Correct content saved for comparison
(maybe hexdump "/mnt/test/junk" vs "/mnt/test/junk.fsxgood")
+++ exited with 110 +++We can see that the bad data is detected in the final MAPREAD operation and and bad offset is at
0x1d000. If we look at the operations dump above its clear that none of the operations should be modifying the0x1d000so we should have been reading 0s but yet we see some junk data there in the file:$ hexdump /mnt/test/junk -s 0x1c000 -n0x1020 001c000 0000 0000 0000 0000 0000 0000 0000 0000 * 001d000 22f3 91f3 7ff3 3af3 39f3 23f3 6df3 c2f3 001d010 c5f3 f6f3 a6f3 1ef3 58f3 40f3 32f3 5ff3 001d020
Quoted 7 lines
Quoted 2 lines
Another thing to not is that I can't reproduce the above on scsi-debug device. @Darrick, @John, could this be an issue in kernel?
Hi Ojaswin,
If we can be sure this's a kernel bug, rather than a fstests (patch) issue, I think we can merge this patchset to expose this bug. Does this make sense to you and others?
Looks like a kernel bug to me...
--D
Quoted 19 lines
Thanks,
ZorroQuoted 14 lines
Regards,
ojaswinQuoted 10 lines
Quoted 10 lines
Thanks,
ZorroQuoted 4 lines
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 267 lines · Darrick J. Wong
Quoted 266 lines · Zorro Lang
Quoted 260 lines · Ojaswin Mujoo
Quoted 179 lines · Ojaswin Mujoo
Quoted 145 lines · Zorro Lang
Quoted 135 lines · Ojaswin Mujoo
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")Quoted 5 lines
Quoted 2 lines
Thanks,
ZorroHi Zorro, just to confirm is this on an older kernel that doesnt support
RWF_ATOMICor on a kernle that does support it.I tested on linux 6.16 and current latest linux v6.17+ (will be 6.18-rc1 later). About the
RWF_ATOMICflag in my system:Quoted 6 lines
# grep -rsn RWF_ATOMIC /usr/include/ /usr/include/bits/uio-ext.h:51:#define RWF_ATOMIC 0x00000040 /* Write is to be issued with torn-write /usr/include/linux/fs.h:424:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040) /usr/include/linux/fs.h:431: RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ /usr/include/xfs/linux.h:236:#ifndef RWF_ATOMIC /usr/include/xfs/linux.h:237:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040)Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Quoted 6 lines
I'm not sure how this will affect the upcoming release, if you want shall I send a small patch to make the atomic writes feature default off instead of default on till we root cause this?
Regards,
OjaswinHi Zorro,
So I'm able to narrow down the opoerations and replicate it via the following replay file:
# -----
# replay.fsxops # ----- write_atomic 0x57000 0x1000 0x69690 write_atomic 0x66000 0x1000 0x4de00 write_atomic 0x18000 0x1000 0x2c800 copy_range 0x20000 0x1000 0xe00 0x70e00 write_atomic 0x18000 0x1000 0x70e00 copy_range 0x21000 0x1000 0x23000 0x74218 truncate 0x0 0x11200 0x4daef * write_atomic 0x43000 0x1000 0x11200 * write_atomic 0x15000 0x1000 0x44000 copy_range 0xd000 0x1000 0x1d800 0x44000 mapread 0x1c000 0x1803 0x1e400 *Command: ./ltp/fsx -N 10000 -o 8192 -l 500000 -r 4096 -t 512 -w 512 -Z -FKuHzI --replay-ops replay.fsxops $MNT/junk
$MNT/junk is always opened
O_TRUNCand is an on an XFS FS where the disk is non-atomic so allRWF_ATOMICwrites are software emulated.Here are the logs generated for this run:
Seed set to 1
main: filesystem does not support exchange range, disabling!READ BAD DATA: offset =
0x1c000, size =0x1803, fname =/mnt/test/junkOFFSET GOOD BAD RANGE 0x1d000 0x0000 0xf322 0x0operation# (mod 256) for the bad data may be 243
0x1d0010x00000x22f30x1
operation# (mod 256) for the bad data may be 2430x1d0020x00000xf3910x2
operation# (mod 256) for the bad data may be 2430x1d0030x00000x91f30x3
<... a few more such lines ..>LOG DUMP (11 total operations):
openat(AT_FDCWD, "/mnt/test/junk.fsxops", O_WRONLY|O_CREAT|O_TRUNC, 0666)= 71( 1 mod 256): WRITE 0x57000 thru 0x57fff (0x1000 bytes) HOLE ***WWWW ATOMIC 2( 2 mod 256): WRITE 0x66000 thru 0x66fff (0x1000 bytes) HOLE ATOMIC 3( 3 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 4( 4 mod 256): COPY 0x20000 thru 0x20fff (0x1000 bytes) to 0xe00 thru 0x1dff 5( 5 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 6( 6 mod 256): COPY 0x21000 thru 0x21fff (0x1000 bytes) to 0x23000 thru 0x23fff 7( 7 mod 256): TRUNCATE DOWN from 0x67000 to 0x11200 ******WWWW 8( 8 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW ATOMIC 9( 9 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) ATOMIC10( 10 mod 256): COPY
0xd000thru0xdfff(0x1000bytes) to0x1d800thru0x1e7ff
11( 11 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR*** Log of operations saved to "/mnt/test/junk.fsxops"; replay with --replay-ops Correct content saved for comparison
(maybe hexdump "/mnt/test/junk" vs "/mnt/test/junk.fsxgood")
+++ exited with 110 +++We can see that the bad data is detected in the final MAPREAD operation and and bad offset is at
0x1d000. If we look at the operations dump above its clear that none of the operations should be modifying the0x1d000so we should have been reading 0s but yet we see some junk data there in the file:$ hexdump /mnt/test/junk -s 0x1c000 -n0x1020 001c000 0000 0000 0000 0000 0000 0000 0000 0000 * 001d000 22f3 91f3 7ff3 3af3 39f3 23f3 6df3 c2f3 001d010 c5f3 f6f3 a6f3 1ef3 58f3 40f3 32f3 5ff3 001d020Quoted 2 lines
Another thing to not is that I can't reproduce the above on scsi-debug device. @Darrick, @John, could this be an issue in kernel?
Hi Ojaswin,
Quoted 4 lines
Quoted 2 lines
If we can be sure this's a kernel bug, rather than a fstests (patch) issue, I think we can merge this patchset to expose this bug. Does this make sense to you and others?
Looks like a kernel bug to me...
Thanks Darrick! If I merge this patchset, the fstests users might hit some unexpected fsx test failures in their regular regression test. I don't want to make panic, if no one mind that, I'll merge it. Or do you want to disable the atomic write fsx test by default currently?
Thanks,
Zorro
Quoted 23 lines
--D
Quoted 19 lines
Thanks,
ZorroQuoted 14 lines
Regards,
ojaswinQuoted 10 lines
Quoted 10 lines
Thanks,
ZorroQuoted 4 lines
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 271 lines · Zorro Lang
Quoted 270 lines · Darrick J. Wong
Quoted 268 lines · Zorro Lang
Quoted 260 lines · Ojaswin Mujoo
Quoted 179 lines · Ojaswin Mujoo
Quoted 145 lines · Zorro Lang
Quoted 135 lines · Ojaswin Mujoo
Quoted 34 lines · Zorro Lang
Quoted 24 lines · Ojaswin Mujoo
Quoted 10 lines · Zorro Lang
Quoted 8 lines · Ojaswin Mujoo
Implement atomic write support to help fuzz atomic writes with fsx.
Suggested-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>---
Quoted 4 lines
Hmm... this patch causes more regular fsx test cases fail on old kernel, (e.g. g/760, g/617, g/263 ...) except set "FSX_AVOID=-a". Is there a way to disable "atomic write" automatically if it's not supported by current system?
Hi Zorro,
Sorry for being late, I've been on vacation this week.Yes so by design we should be automatically disabling atomic writes when they are not supported by the stack but seems like the issue is that when we do disable it we print some extra messages to stdout/err which show up in the xfstests output causing failure.
Quoted 8 lines
I can think of 2 ways around this:
1. Don't print anything and just silently drop atomic writes if stack doesn't support them.
2. Make atomic writes as a default off instead of default on feature but his loses a bit of coverage as existing tests wont get atomic write testing free of cost any more.
Quoted 6 lines
Hi Ojaswin,
Please have a nice vacation :)
It's not the "extra messages" cause failure, those "quiet" failures can be fixed by:
Oh okay got it.
Quoted 37 lines
ltp/fsx.c +6
-4@@ -1847,8 +1847,9 @@ int test_atomic_writes(void) {struct statx stx;if (o_direct !=O_DIRECT ) { -fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " -"disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: atomic writes need O_DIRECT (-Z), " +"disabling!\n" );return 0 ; }@@ -1867,8 +1868,9 @@ int test_atomic_writes(void) {return 1 ; } -fprintf (stderr,"main: IO Stack does not support " -"atomic writes, disabling!\n" ); +if (!quiet) +fprintf (stderr,"main: IO Stack does not support " +"atomic writes, disabling!\n" );return 0 ; }But I hit more read or write failures e.g. [1], this failure can't be reproduced with FSX_AVOID=-a. Is it a atomic write bug or an unexpected test failure?
Thanks,
Zorro<...>
Quoted 51 lines
+244(244 mod 256): SKIPPED (no operation)
+245(245 mod 256): FALLOC0x695c5thru0x6a2e6(0xd21bytes) INTERIOR
+246(246 mod 256): MAPWRITE0x5ac00thru0x5b185(0x586bytes)
+247(247 mod 256): WRITE0x31200thru0x313ff(0x200bytes)
+248(248 mod 256): SKIPPED (no operation)
+249(249 mod 256): TRUNCATE DOWN from0x78242to0xf200******WWWW
+250(250 mod 256): FALLOC0x65000thru0x66f26(0x1f26bytes)PAST_EOF
+251(251 mod 256): WRITE0x45400thru0x467ff(0x1400bytes) HOLE ***WWWW
+252(252 mod 256): SKIPPED (no operation)
+253(253 mod 256): SKIPPED (no operation)
+254(254 mod 256): MAPWRITE0x4be00thru0x4daee(0x1cefbytes)
+255(255 mod 256): MAPREAD0xc000thru0xcae9(0xaeabytes)+256( 0 mod 256): READ 0x3e000 thru 0x3efff (0x1000 bytes) +257( 1 mod 256): SKIPPED (no operation) +258( 2 mod 256): INSERT 0x45000 thru 0x45fff (0x1000 bytes) +259( 3 mod 256): ZERO 0x1d7d5 thru 0x1f399 (0x1bc5 bytes) ******ZZZZ +260( 4 mod 256): TRUNCATE DOWN from 0x4eaef to 0x11200 ******WWWW +261( 5 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW +262( 6 mod 256): WRITE 0x2200 thru 0x31ff (0x1000 bytes) +263( 7 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) +264( 8 mod 256): WRITE 0x2e400 thru 0x2e7ff (0x400 bytes) +265( 9 mod 256): COPY 0xd000 thru 0xdfff (0x1000 bytes) to 0x1d800 thru 0x1e7ff ******EEEE+266( 10 mod 256): CLONE
0x2a000thru0x2afff(0x1000bytes) to0x21000thru0x21fff
+267( 11 mod 256): MAPREAD0x31000thru0x31d0a(0xd0bbytes)
+268( 12 mod 256): SKIPPED (no operation)
+269( 13 mod 256): WRITE0x25000thru0x25fff(0x1000bytes)
+270( 14 mod 256): SKIPPED (no operation)+271( 15 mod 256): MAPREAD 0x30000 thru 0x30577 (0x578 bytes) +272( 16 mod 256): PUNCH 0x1a267 thru 0x1c093 (0x1e2d bytes) +273( 17 mod 256): MAPREAD 0x1f000 thru 0x1f9c9 (0x9ca bytes) +274( 18 mod 256): WRITE 0x40800 thru 0x40dff (0x600 bytes)+275( 19 mod 256): SKIPPED (no operation)
+276( 20 mod 256): MAPWRITE0x20600thru0x22115(0x1b16bytes)
+277( 21 mod 256): MAPWRITE0x3d000thru0x3ee5a(0x1e5bbytes)
+278( 22 mod 256): WRITE0x2ee00thru0x2efff(0x200bytes)
+279( 23 mod 256): WRITE0x76200thru0x769ff(0x800bytes) HOLE
+280( 24 mod 256): SKIPPED (no operation)
+281( 25 mod 256): SKIPPED (no operation)
+282( 26 mod 256): MAPREAD0xa000thru0xa5e7(0x5e8bytes)
+283( 27 mod 256): SKIPPED (no operation)
+284( 28 mod 256): SKIPPED (no operation)
+285( 29 mod 256): SKIPPED (no operation)
+286( 30 mod 256): SKIPPED (no operation)
+287( 31 mod 256): COLLAPSE0x11000thru0x11fff(0x1000bytes)
+288( 32 mod 256): COPY0x5d000thru0x5dfff(0x1000bytes) to0x4ca00thru0x4d9ff
+289( 33 mod 256): TRUNCATE DOWN from0x75a00to0x1e400
+290( 34 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR***ltp/fsx.c +3
-0+Log of operations saved to "/mnt/xfstests/test/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/xfstests/test/junk" vs "/mnt/xfstests/test/junk.fsxgood")Quoted 5 lines
Quoted 2 lines
Thanks,
ZorroHi Zorro, just to confirm is this on an older kernel that doesnt support
RWF_ATOMICor on a kernle that does support it.I tested on linux 6.16 and current latest linux v6.17+ (will be 6.18-rc1 later). About the
RWF_ATOMICflag in my system:Quoted 6 lines
# grep -rsn RWF_ATOMIC /usr/include/ /usr/include/bits/uio-ext.h:51:#define RWF_ATOMIC 0x00000040 /* Write is to be issued with torn-write /usr/include/linux/fs.h:424:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040) /usr/include/linux/fs.h:431: RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ /usr/include/xfs/linux.h:236:#ifndef RWF_ATOMIC /usr/include/xfs/linux.h:237:#define RWF_ATOMIC ((__kernel_rwf_t)0x00000040)Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Quoted 6 lines
I'm not sure how this will affect the upcoming release, if you want shall I send a small patch to make the atomic writes feature default off instead of default on till we root cause this?
Regards,
OjaswinHi Zorro,
So I'm able to narrow down the opoerations and replicate it via the following replay file:
# -----
# replay.fsxops # ----- write_atomic 0x57000 0x1000 0x69690 write_atomic 0x66000 0x1000 0x4de00 write_atomic 0x18000 0x1000 0x2c800 copy_range 0x20000 0x1000 0xe00 0x70e00 write_atomic 0x18000 0x1000 0x70e00 copy_range 0x21000 0x1000 0x23000 0x74218 truncate 0x0 0x11200 0x4daef * write_atomic 0x43000 0x1000 0x11200 * write_atomic 0x15000 0x1000 0x44000 copy_range 0xd000 0x1000 0x1d800 0x44000 mapread 0x1c000 0x1803 0x1e400 *Command: ./ltp/fsx -N 10000 -o 8192 -l 500000 -r 4096 -t 512 -w 512 -Z -FKuHzI --replay-ops replay.fsxops $MNT/junk
$MNT/junk is always opened
O_TRUNCand is an on an XFS FS where the disk is non-atomic so allRWF_ATOMICwrites are software emulated.Here are the logs generated for this run:
Seed set to 1
main: filesystem does not support exchange range, disabling!READ BAD DATA: offset =
0x1c000, size =0x1803, fname =/mnt/test/junkOFFSET GOOD BAD RANGE 0x1d000 0x0000 0xf322 0x0operation# (mod 256) for the bad data may be 243
0x1d0010x00000x22f30x1
operation# (mod 256) for the bad data may be 2430x1d0020x00000xf3910x2
operation# (mod 256) for the bad data may be 2430x1d0030x00000x91f30x3
<... a few more such lines ..>LOG DUMP (11 total operations):
openat(AT_FDCWD, "/mnt/test/junk.fsxops", O_WRONLY|O_CREAT|O_TRUNC, 0666)= 71( 1 mod 256): WRITE 0x57000 thru 0x57fff (0x1000 bytes) HOLE ***WWWW ATOMIC 2( 2 mod 256): WRITE 0x66000 thru 0x66fff (0x1000 bytes) HOLE ATOMIC 3( 3 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 4( 4 mod 256): COPY 0x20000 thru 0x20fff (0x1000 bytes) to 0xe00 thru 0x1dff 5( 5 mod 256): WRITE 0x18000 thru 0x18fff (0x1000 bytes) ATOMIC 6( 6 mod 256): COPY 0x21000 thru 0x21fff (0x1000 bytes) to 0x23000 thru 0x23fff 7( 7 mod 256): TRUNCATE DOWN from 0x67000 to 0x11200 ******WWWW 8( 8 mod 256): WRITE 0x43000 thru 0x43fff (0x1000 bytes) HOLE ***WWWW ATOMIC 9( 9 mod 256): WRITE 0x15000 thru 0x15fff (0x1000 bytes) ATOMIC10( 10 mod 256): COPY
0xd000thru0xdfff(0x1000bytes) to0x1d800thru0x1e7ff
11( 11 mod 256): MAPREAD0x1c000thru0x1d802(0x1803bytes) ***RRRR*** Log of operations saved to "/mnt/test/junk.fsxops"; replay with --replay-ops Correct content saved for comparison
(maybe hexdump "/mnt/test/junk" vs "/mnt/test/junk.fsxgood")
+++ exited with 110 +++We can see that the bad data is detected in the final MAPREAD operation and and bad offset is at
0x1d000. If we look at the operations dump above its clear that none of the operations should be modifying the0x1d000so we should have been reading 0s but yet we see some junk data there in the file:$ hexdump /mnt/test/junk -s 0x1c000 -n0x1020 001c000 0000 0000 0000 0000 0000 0000 0000 0000 * 001d000 22f3 91f3 7ff3 3af3 39f3 23f3 6df3 c2f3 001d010 c5f3 f6f3 a6f3 1ef3 58f3 40f3 32f3 5ff3 001d020Quoted 2 lines
Another thing to not is that I can't reproduce the above on scsi-debug device. @Darrick, @John, could this be an issue in kernel?
Hi Ojaswin,
If we can be sure this's a kernel bug, rather than a fstests (patch) issue, I think we can merge this patchset to expose this bug. Does this make sense to you and others?
Quoted 6 lines
Quoted 1 line
Looks like a kernel bug to me...
Thanks Darrick! If I merge this patchset, the fstests users might hit some unexpected fsx test failures in their regular regression test. I don't want to make panic, if no one mind that, I'll merge it. Or do you want to disable the atomic write fsx test by default currently?
Nah, leave it enabled, or else nobody will be testing it.
--D
Quoted 28 lines
Thanks,
ZorroQuoted 23 lines
--D
Quoted 19 lines
Thanks,
ZorroQuoted 14 lines
Regards,
ojaswinQuoted 10 lines
Quoted 10 lines
Thanks,
ZorroQuoted 4 lines
Regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
In reply to Ojaswin MujooQuoted 22 lines · Ojaswin Mujoo
Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).
Quoted 3 lines
I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Hi Ojaswin,
Sorry for the very slow response.
Are you still checking this issue?
To replicate, should I just take latest xfs kernel and run this series on top of latest xfstests? Is it 100% reproducible?
Thanks,
John
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 32 lines · John Garry
Quoted 22 lines · Ojaswin Mujoo
Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).Quoted 3 lines
I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Hi Ojaswin,
Sorry for the very slow response.
Quoted 7 lines
Are you still checking this issue?
To replicate, should I just take latest xfs kernel and run this series on top of latest xfstests? Is it 100% reproducible?
Thanks,
John
Hi John,
Yes Im looking into it but I'm now starting to run into some reflink/cow based concepts that are taking time to understand. Let me share what I have till now:
So the test.sh that I'm using can be found here [1] which just uses an fsx replay file (which replays all operations) present in the same repo [2]. If you see the replay file, there are a bunch of random operations followed by the last 2 commented out operations:
# copy_range 0xd000 0x1000 0x1d800 0x44000 <--- # operations <start> <len> <dest of copy> <filesize (can be ignored)> # mapread 0x1e000 0x1000 0x1e400 *
The copy_range here is the one which causes (or exposes) the corruption at 0x1e800 (the end of copy range destination gets corrupted).
To have more control, I commented these 2 operations and am doing it by hand in the test.sh file, with xfs_io. I'm also using a non atomic write device so we only have S/W fallback.
Now some observations:
1. The copy_range operations is actually copying from a hole to a hole, so we should be reading all 0s. But What I see is the following happening:
vfs_copy_file_range
do_splice_direct
do_splice_direct_actor
do_splice_read
# Adds the folio at src offset to the pipe. I confirmed this is all 0x0.
splice_direct_to_actor
direct_splice_actor
do_splice_from
iter_file_splice_write
xfs_file_write_iter
xfs_file_buffered_write
iomap_file_buferred_write
iomap_iter
xfs_buferred_write_iomap_begin
# Here we correctly see that there is noting at the # destination in data fork, but somehow we find a mapped # extent in cow fork which is returned to iomap.
iomap_write_iter
__iomap_write_begin
# Here we notice folio is not uptodate and call
# iomap_read_folio_range() to read from the cow_fork # mapping we found earlier. This results in folio having # incorrect data at 0x1e800 offset.
So it seems like the fsx operations might be corrupting the cow fork state somehow leading to stale data exposure.
2. If we disable atomic writes we dont hit the issue.
3. If I do a -c pread of the destination range before doing the copy_range operation then I don't see the corruption any more.
I'm now trying to figure out why the mapping returned is not IOMAP_HOLE as it should be. I don't know the COW path in xfs so there are some gaps in my understanding. Let me know if you need any other information since I'm reliably able to replicate on 6.17.0-rc4.
[1]
https://github.com/OjaswinM/fsx-aw-issue/tree/master
[2] https://github.com/OjaswinM/fsx-aw-issue/blob/master/repro.fsxops
regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 96 lines · Ojaswin Mujoo
Quoted 32 lines · John Garry
Quoted 22 lines · Ojaswin Mujoo
Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).Quoted 3 lines
I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Hi Ojaswin,
Sorry for the very slow response.
Quoted 7 lines
Are you still checking this issue?
To replicate, should I just take latest xfs kernel and run this series on top of latest xfstests? Is it 100% reproducible?
Thanks,
JohnHi John,
Yes Im looking into it but I'm now starting to run into some reflink/cow based concepts that are taking time to understand. Let me share what I have till now:
So the
test.shthat I'm using can be found here [1] which just uses an fsx replay file (which replays all operations) present in the same repo [2]. If you see the replay file, there are a bunch of random operations followed by the last 2 commented out operations:# copy_range 0xd000 0x1000 0x1d800 0x44000 <--- # operations <start> <len> <dest of copy> <filesize (can be ignored)> # mapread 0x1e000 0x1000 0x1e400 *The
copy_rangehere is the one which causes (or exposes) the corruption at0x1e800(the end of copy range destination gets corrupted).To have more control, I commented these 2 operations and am doing it by hand in the
test.shfile, withxfs_io. I'm also using a non atomic write device so we only have S/W fallback.Now some observations:
1. The
copy_rangeoperations is actually copying from a hole to a hole, so we should be reading all 0s. But What I see is the following happening:
vfs_copy_file_range
do_splice_direct
do_splice_direct_actor
do_splice_read
# Adds the folio at src offset to the pipe. I confirmed this is all0x0.
splice_direct_to_actor
direct_splice_actor
do_splice_from
iter_file_splice_write
xfs_file_write_iter
xfs_file_buffered_write
iomap_file_buferred_write
iomap_iter
xfs_buferred_write_iomap_begin
# Here we correctly see that there is noting at the # destination in data fork, but somehow we find a mapped # extent in cow fork which is returned to iomap.
iomap_write_iter
__iomap_write_begin
# Here we notice folio is not uptodate and call
#iomap_read_folio_range()to read from thecow_fork# mapping we found earlier. This results in folio having # incorrect data at0x1e800offset.So it seems like the fsx operations might be corrupting the cow fork state somehow leading to stale data exposure.
2. If we disable atomic writes we dont hit the issue.
Quoted 8 lines
3. If I do a -c pread of the destination range before doing the
copy_rangeoperation then I don't see the corruption any more.I'm now trying to figure out why the mapping returned is not
IOMAP_HOLEas it should be. I don't know the COW path in xfs so there are some gaps in my understanding. Let me know if you need any other information since I'm reliably able to replicate on 6.17.0-rc4.
I haven't followed your issue closely, but just on this hole vs. COW thing, XFS has a bit of a quirk where speculative COW fork preallocation can expand out over holes in the data fork. If iomap lookup for buffered write sees COW fork blocks present, it reports those blocks as the primary mapping even if the data fork happens to be a hole (since there's no point in allocating blocks to the data fork when we can just remap).
Again I've no idea if this relates to your issue or what you're referring to as a hole (i.e. data fork only?), but just pointing it out. The latest iomap/xfs patches I posted a few days ago kind of dance around this a bit, but I was somewhat hoping that maybe the cleanups there would trigger some thoughts on better iomap reporting in that regard.
Brian
Quoted 8 lines
[1]
https://github.com/OjaswinM/fsx-aw-issue/tree/master[2] https://github.com/OjaswinM/fsx-aw-issue/blob/master/repro.fsxops
regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 114 lines · Brian Foster
Quoted 96 lines · Ojaswin Mujoo
Quoted 32 lines · John Garry
Quoted 22 lines · Ojaswin Mujoo
Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).Quoted 3 lines
I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Hi Ojaswin,
Sorry for the very slow response.
Quoted 7 lines
Are you still checking this issue?
To replicate, should I just take latest xfs kernel and run this series on top of latest xfstests? Is it 100% reproducible?
Thanks,
JohnHi John,
Yes Im looking into it but I'm now starting to run into some reflink/cow based concepts that are taking time to understand. Let me share what I have till now:
So the
test.shthat I'm using can be found here [1] which just uses an fsx replay file (which replays all operations) present in the same repo [2]. If you see the replay file, there are a bunch of random operations followed by the last 2 commented out operations:# copy_range 0xd000 0x1000 0x1d800 0x44000 <--- # operations <start> <len> <dest of copy> <filesize (can be ignored)> # mapread 0x1e000 0x1000 0x1e400 *The
copy_rangehere is the one which causes (or exposes) the corruption at0x1e800(the end of copy range destination gets corrupted).To have more control, I commented these 2 operations and am doing it by hand in the
test.shfile, withxfs_io. I'm also using a non atomic write device so we only have S/W fallback.Now some observations:
1. The
copy_rangeoperations is actually copying from a hole to a hole, so we should be reading all 0s. But What I see is the following happening:
vfs_copy_file_range
do_splice_direct
do_splice_direct_actor
do_splice_read
# Adds the folio at src offset to the pipe. I confirmed this is all0x0.
splice_direct_to_actor
direct_splice_actor
do_splice_from
iter_file_splice_write
xfs_file_write_iter
xfs_file_buffered_write
iomap_file_buferred_write
iomap_iter
xfs_buferred_write_iomap_begin
# Here we correctly see that there is noting at the # destination in data fork, but somehow we find a mapped # extent in cow fork which is returned to iomap.
iomap_write_iter
__iomap_write_begin
# Here we notice folio is not uptodate and call
#iomap_read_folio_range()to read from thecow_fork# mapping we found earlier. This results in folio having # incorrect data at0x1e800offset.So it seems like the fsx operations might be corrupting the cow fork state somehow leading to stale data exposure.
2. If we disable atomic writes we dont hit the issue.
Quoted 8 lines
3. If I do a -c pread of the destination range before doing the
copy_rangeoperation then I don't see the corruption any more.I'm now trying to figure out why the mapping returned is not
IOMAP_HOLEas it should be. I don't know the COW path in xfs so there are some gaps in my understanding. Let me know if you need any other information since I'm reliably able to replicate on 6.17.0-rc4.I haven't followed your issue closely, but just on this hole vs. COW thing, XFS has a bit of a quirk where speculative COW fork preallocation can expand out over holes in the data fork. If iomap lookup for buffered write sees COW fork blocks present, it reports those blocks as the primary mapping even if the data fork happens to be a hole (since there's no point in allocating blocks to the data fork when we can just remap).
Quoted 6 lines
Again I've no idea if this relates to your issue or what you're referring to as a hole (i.e. data fork only?), but just pointing it out. The latest iomap/xfs patches I posted a few days ago kind of dance around this a bit, but I was somewhat hoping that maybe the cleanups there would trigger some thoughts on better iomap reporting in that regard.
Hi Brian, Thanks for the details and yes by "hole" i did mean hole in data fork only. The part that I'm now confused about is does this sort of preallocation extent hold any valid data? IIUC it should not, so I would expect it to trigger iomap_block_needs_zeroing() to write zeroes to the folio. Instead, what I see in the issue is that we are trying to do disk read.
Regards,
ojaswin
Quoted 13 lines
Brian
Quoted 8 lines
[1]
https://github.com/OjaswinM/fsx-aw-issue/tree/master[2] https://github.com/OjaswinM/fsx-aw-issue/blob/master/repro.fsxops
regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 93 lines · Ojaswin Mujoo
Quoted 92 lines · Brian Foster
Quoted 91 lines · Ojaswin Mujoo
Quoted 32 lines · John Garry
Quoted 22 lines · Ojaswin Mujoo
Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).Quoted 3 lines
I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Hi Ojaswin,
Sorry for the very slow response.
Quoted 7 lines
Are you still checking this issue?
To replicate, should I just take latest xfs kernel and run this series on top of latest xfstests? Is it 100% reproducible?
Thanks,
JohnHi John,
Yes Im looking into it but I'm now starting to run into some reflink/cow based concepts that are taking time to understand. Let me share what I have till now:
So the
test.shthat I'm using can be found here [1] which just uses an fsx replay file (which replays all operations) present in the same repo [2]. If you see the replay file, there are a bunch of random operations followed by the last 2 commented out operations:# copy_range 0xd000 0x1000 0x1d800 0x44000 <--- # operations <start> <len> <dest of copy> <filesize (can be ignored)> # mapread 0x1e000 0x1000 0x1e400 *The
copy_rangehere is the one which causes (or exposes) the corruption at0x1e800(the end of copy range destination gets corrupted).To have more control, I commented these 2 operations and am doing it by hand in the
test.shfile, withxfs_io. I'm also using a non atomic write device so we only have S/W fallback.Now some observations:
1. The
copy_rangeoperations is actually copying from a hole to a hole, so we should be reading all 0s. But What I see is the following happening:
vfs_copy_file_range
do_splice_direct
do_splice_direct_actor
do_splice_read
# Adds the folio at src offset to the pipe. I confirmed this is all0x0.
splice_direct_to_actor
direct_splice_actor
do_splice_from
iter_file_splice_write
xfs_file_write_iter
xfs_file_buffered_write
iomap_file_buferred_write
iomap_iter
xfs_buferred_write_iomap_begin
# Here we correctly see that there is noting at the # destination in data fork, but somehow we find a mapped # extent in cow fork which is returned to iomap.
iomap_write_iter
__iomap_write_begin
# Here we notice folio is not uptodate and call
#iomap_read_folio_range()to read from thecow_fork# mapping we found earlier. This results in folio having # incorrect data at0x1e800offset.
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
So it seems like the fsx operations might be corrupting the cow fork state somehow leading to stale data exposure.
2. If we disable atomic writes we dont hit the issue.
3. If I do a -c pread of the destination range before doing the
copy_rangeoperation then I don't see the corruption any more.
Yeah, I stopped seeing failures after adding -X (verify data after every operation) to FSX_AVOID.
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
I'm now trying to figure out why the mapping returned is not
IOMAP_HOLEas it should be. I don't know the COW path in xfs so there are some gaps in my understanding. Let me know if you need any other information since I'm reliably able to replicate on 6.17.0-rc4.
Quoted 7 lines
Quoted 7 lines
I haven't followed your issue closely, but just on this hole vs. COW thing, XFS has a bit of a quirk where speculative COW fork preallocation can expand out over holes in the data fork. If iomap lookup for buffered write sees COW fork blocks present, it reports those blocks as the primary mapping even if the data fork happens to be a hole (since there's no point in allocating blocks to the data fork when we can just remap).
That sounds like a bug -- if a sub-fsblock write to an uncached file range has to read data in from disk, then xfs needs to pass the data fork mapping to iomap even if it's a read.
Can you capture the ftrace output of the iomap_iter_*map tracepoints?
Quoted 10 lines
Quoted 6 lines
Again I've no idea if this relates to your issue or what you're referring to as a hole (i.e. data fork only?), but just pointing it out. The latest iomap/xfs patches I posted a few days ago kind of dance around this a bit, but I was somewhat hoping that maybe the cleanups there would trigger some thoughts on better iomap reporting in that regard.
Hi Brian, Thanks for the details and yes by "hole" i did mean hole in data fork only. The part that I'm now confused about is does this sort of preallocation extent hold any valid data? IIUC it should not, so I
No. Mappings in the cow fork are not fully written and should never be used for reads.
Quoted 3 lines
would expect it to trigger
iomap_block_needs_zeroing()to write zeroes to the folio. Instead, what I see in the issue is that we are trying to do disk read.
Hrm. Part of the problem here might be that iomap_read_folio_range ignores iomap_iter::srcmap if it's type IOMAP_HOLE (see iomap_iter_srcmap), even if the filesystem actually *set* the srcmap to a hole.
FWIW I see a somewhat different failure -- not data corruption, but pwrite returning failure:
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 renamed from run/fstests/bin/tests/generic/521.out 2025-07-15 14:45:15.100315255 -0700 +22 -0
@@ -1,2 +1,668 @@ QA output created by 521 +dowrite: write: Input/output error +LOG DUMP (661 total operations): +1( 1 mod 256): TRUNCATE UP from 0x0 to 0x1d000 +2( 2 mod 256): DEDUPE 0x19000 thru 0x1bfff (0x3000 bytes) to 0x13000 thru 0x15fff +3( 3 mod 256): SKIPPED (no operation) +4( 4 mod 256): PUNCH 0x5167 thru 0x12d1c (0xdbb6 bytes) +5( 5 mod 256): WRITE 0x79000 thru 0x86fff (0xe000 bytes) HOLE +6( 6 mod 256): PUNCH 0x32344 thru 0x36faf (0x4c6c bytes) +7( 7 mod 256): READ 0x0 thru 0xfff (0x1000 bytes) +8( 8 mod 256): WRITE 0xe000 thru 0x11fff (0x4000 bytes) +9( 9 mod 256): PUNCH 0x71324 thru 0x86fff (0x15cdc bytes) +10( 10 mod 256): MAPREAD 0x5b000 thru 0x6d218 (0x12219 bytes) +11( 11 mod 256): COLLAPSE 0x70000 thru 0x79fff (0xa000 bytes) +12( 12 mod 256): WRITE 0x41000 thru 0x50fff (0x10000 bytes) +13( 13 mod 256): INSERT 0x39000 thru 0x4dfff (0x15000 bytes) +14( 14 mod 256): WRITE 0x34000 thru 0x37fff (0x4000 bytes) +15( 15 mod 256): MAPREAD 0x55000 thru 0x6ee44 (0x19e45 bytes) +16( 16 mod 256): READ 0x46000 thru 0x55fff (0x10000 bytes) +17( 17 mod 256): PUNCH 0x1ccea thru 0x23b2e (0x6e45 bytes) +18( 18 mod 256): COPY 0x2a000 thru 0x35fff (0xc000 bytes) to 0x52000 thru 0x5dfff +19( 19 mod 256): SKIPPED (no operation) +20( 20 mod 256): WRITE 0x10000 thru 0x1ffff (0x10000 bytes)
<snip>
+645(133 mod 256): READ 0x5000 thru 0x16fff (0x12000 bytes) +646(134 mod 256): PUNCH 0x3a51d thru 0x41978 (0x745c bytes) +647(135 mod 256): FALLOC 0x47f4c thru 0x54867 (0xc91b bytes) INTERIOR +648(136 mod 256): WRITE 0xa000 thru 0x1dfff (0x14000 bytes) +649(137 mod 256): CLONE 0x83000 thru 0x89fff (0x7000 bytes) to 0x4b000 thru 0x51fff +650(138 mod 256): TRUNCATE DOWN from 0x8bac4 to 0x7e000 +651(139 mod 256): MAPWRITE 0x13000 thru 0x170e6 (0x40e7 bytes) +652(140 mod 256): XCHG 0x6a000 thru 0x7cfff (0x13000 bytes) to 0x8000 thru 0x1afff +653(141 mod 256): XCHG 0x35000 thru 0x3cfff (0x8000 bytes) to 0x1b000 thru 0x22fff +654(142 mod 256): CLONE 0x47000 thru 0x60fff (0x1a000 bytes) to 0x65000 thru 0x7efff +655(143 mod 256): DEDUPE 0x79000 thru 0x7dfff (0x5000 bytes) to 0x6e000 thru 0x72fff +656(144 mod 256): XCHG 0x4d000 thru 0x5ffff (0x13000 bytes) to 0x8000 thru 0x1afff +657(145 mod 256): PUNCH 0x7194f thru 0x7efff (0xd6b1 bytes) +658(146 mod 256): PUNCH 0x7af7e thru 0x7efff (0x4082 bytes) +659(147 mod 256): MAPREAD 0x77000 thru 0x7e55d (0x755e bytes) +660(148 mod 256): READ 0x58000 thru 0x64fff (0xd000 bytes) +661(149 mod 256): WRITE 0x88000 thru 0x8bfff (0x4000 bytes) HOLE
+Log of operations saved to "/mnt/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison
+(maybe hexdump "/mnt/junk" vs "/mnt/junk.fsxgood")
Curiously there are no EIO errors logged in dmesg.
--D
Quoted 16 lines
Regards,
ojaswinQuoted 13 lines
Brian
Quoted 8 lines
[1]
https://github.com/OjaswinM/fsx-aw-issue/tree/master[2] https://github.com/OjaswinM/fsx-aw-issue/blob/master/repro.fsxops
regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 119 lines · Darrick J. Wong
Quoted 93 lines · Ojaswin Mujoo
Quoted 92 lines · Brian Foster
Quoted 91 lines · Ojaswin Mujoo
Quoted 32 lines · John Garry
Quoted 22 lines · Ojaswin Mujoo
Hi Zorro, thanks for checking this. So correct me if im wrong but I understand that you have run this test on an atomic writes enabled kernel where the stack also supports atomic writes.
Looking at the bad data log:
+READ BAD DATA: offset = 0x1c000, size = 0x1803, fname = /mnt/xfstests/test/junk +OFFSET GOOD BAD RANGE +0x1c000 0x0000 0xcdcd 0x0 +operation# (mod 256) for the bad data may be 205We see that
0x0000was expected but we got0xcdcd. Now the operation that caused this is indicated to be 205, but looking at that operation:+205(205 mod 256): ZERO
0x6dbe6thru0x6e6aa(0xac5bytes)This doesn't even overlap the range that is bad. (
0x1c000to0x1c00f). Infact, it does seem like an unlikely coincidence that the actual data in the bad range is0xcdcdwhich is somethingxfs_io-c "pwrite" writes to default (fsx writes random data in even offsets and operation num in odd).Quoted 3 lines
I am able to replicate this but only on XFS but not on ext4 (atleast not in 20 runs). I'm trying to better understand if this is a test issue or not. Will keep you update.
Hi Ojaswin,
Sorry for the very slow response.
Quoted 7 lines
Are you still checking this issue?
To replicate, should I just take latest xfs kernel and run this series on top of latest xfstests? Is it 100% reproducible?
Thanks,
JohnHi John,
Yes Im looking into it but I'm now starting to run into some reflink/cow based concepts that are taking time to understand. Let me share what I have till now:
So the
test.shthat I'm using can be found here [1] which just uses an fsx replay file (which replays all operations) present in the same repo [2]. If you see the replay file, there are a bunch of random operations followed by the last 2 commented out operations:# copy_range 0xd000 0x1000 0x1d800 0x44000 <--- # operations <start> <len> <dest of copy> <filesize (can be ignored)> # mapread 0x1e000 0x1000 0x1e400 *The
copy_rangehere is the one which causes (or exposes) the corruption at0x1e800(the end of copy range destination gets corrupted).To have more control, I commented these 2 operations and am doing it by hand in the
test.shfile, withxfs_io. I'm also using a non atomic write device so we only have S/W fallback.Now some observations:
1. The
copy_rangeoperations is actually copying from a hole to a hole, so we should be reading all 0s. But What I see is the following happening:
vfs_copy_file_range
do_splice_direct
do_splice_direct_actor
do_splice_read
# Adds the folio at src offset to the pipe. I confirmed this is all0x0.
splice_direct_to_actor
direct_splice_actor
do_splice_from
iter_file_splice_write
xfs_file_write_iter
xfs_file_buffered_write
iomap_file_buferred_write
iomap_iter
xfs_buferred_write_iomap_begin
# Here we correctly see that there is noting at the # destination in data fork, but somehow we find a mapped # extent in cow fork which is returned to iomap.
iomap_write_iter
__iomap_write_begin
# Here we notice folio is not uptodate and call
#iomap_read_folio_range()to read from thecow_fork# mapping we found earlier. This results in folio having # incorrect data at0x1e800offset.Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
So it seems like the fsx operations might be corrupting the cow fork state somehow leading to stale data exposure.
2. If we disable atomic writes we dont hit the issue.
3. If I do a -c pread of the destination range before doing the
copy_rangeoperation then I don't see the corruption any more.Yeah, I stopped seeing failures after adding -X (verify data after every operation) to
FSX_AVOID.Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
I'm now trying to figure out why the mapping returned is not
IOMAP_HOLEas it should be. I don't know the COW path in xfs so there are some gaps in my understanding. Let me know if you need any other information since I'm reliably able to replicate on 6.17.0-rc4.Quoted 7 lines
Quoted 7 lines
I haven't followed your issue closely, but just on this hole vs. COW thing, XFS has a bit of a quirk where speculative COW fork preallocation can expand out over holes in the data fork. If iomap lookup for buffered write sees COW fork blocks present, it reports those blocks as the primary mapping even if the data fork happens to be a hole (since there's no point in allocating blocks to the data fork when we can just remap).
Quoted 5 lines
That sounds like a bug -- if a sub-fsblock write to an uncached file range has to read data in from disk, then xfs needs to pass the data fork mapping to iomap even if it's a read.
Can you capture the ftrace output of the iomap_iter_*map tracepoints?
Hey Darrick, below are the details:
Command:
sudo ./xfstests-dev/ltp/fsx -N 10000 -o 8192 -l 500000 -r 4096 -t 512 -w 512 -Z -FKuHzI -g B --replay-ops prep.fsxops /mnt/test/junk sudo perf record -e 'iomap:iomap_iter_*map' xfs_io -c 'copy_range -s 0xd000 -d 0x1d800 -l 0x1000 /mnt/test/junk' -c 'pread -v 0x1e7f0 0x20' /mnt/test/junk (prep.fsxops is here: https://github.com/OjaswinM/fsx-aw-issue/blob/master/repro.fsxops)
stdout:
Seed set to 1 main: filesystem does not support exchange range, disabling! truncating to largest ever: 0x50e00 truncating to largest ever: 0x70e00 All 136 operations completed A-OK! 0001e7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0001e800: 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB read 16/16 bytes at offset 124927 16.000000 bytes, 1 ops; 0.0000 sec (339.674 KiB/sec and 21739.1304 ops/sec) [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.003 MB perf.data (1 samples) ]
perf output:
xfs_io 981 [000] 254.331537: iomap:iomap_iter_dstmap: dev 254:32 ino 0x83 bdev 254:32 addr 0xb8000 offset 0x1d000 length 0x23000 type MAPPED (0x2) flags DIRTY|SHARED (0x6)
perf output if I call -c pread before -c copy_range:
xfs_io 1098 [000] 911.542373: iomap:iomap_iter_dstmap: dev 254:32 ino 0x83 bdev 254:32 addr 0xffffffffffffffff offset 0x1e000 length 0x1000 type HOLE (0x0) flags DIRTY (0x2) xfs_io 1098 [000] 911.542776: iomap:iomap_iter_dstmap: dev 254:32 ino 0x83 bdev 254:32 addr 0xb8000 offset 0x1d000 length 0x23000 type MAPPED (0x2) flags DIRTY|SHARED (0x6)
(The first event is from -c pread)
Fiemap before and after output incase it helps:
$ xfs_io -c 'fiemap -v' -c 'copy_range -s 0xd000 -d 0x1d800 -l 0x1000 /mnt/test/junk' -c 'pread -v 0x1e7f0 0x20' -c 'fiemap -v' /mnt/test/junk
/mnt/test/junk: EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS 0: [0..7]: 408..415 8 0x0 1: [8..31]: 520..543 24 0x0 2: [32..167]: hole 136 3: [168..175]: 1408..1415 8 0x0 4: [176..367]: hole 192 5: [368..375]: 1608..1615 8 0x0 6: [376..535]: hole 160 7: [536..543]: 1048..1055 8 0x1 0001e7f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0001e800: 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB read 32/32 bytes at offset 124912 32.000000 bytes, 1 ops; 0.0000 sec (325.521 KiB/sec and 10416.6667 ops/sec) /mnt/test/junk: EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS 0: [0..7]: 408..415 8 0x0 1: [8..31]: 520..543 24 0x0 2: [32..167]: hole 136 3: [168..175]: 1408..1415 8 0x0 4: [176..231]: hole 56 5: [232..247]: 1472..1487 16 0x0 6: [248..367]: hole 120 7: [368..375]: 1608..1615 8 0x0 8: [376..535]: hole 160 9: [536..543]: 1048..1055 8 0x1
Quoted 31 lines
Quoted 11 lines
Quoted 7 lines
Again I've no idea if this relates to your issue or what you're referring to as a hole (i.e. data fork only?), but just pointing it out. The latest iomap/xfs patches I posted a few days ago kind of dance around this a bit, but I was somewhat hoping that maybe the cleanups there would trigger some thoughts on better iomap reporting in that regard.
Hi Brian, Thanks for the details and yes by "hole" i did mean hole in data fork only. The part that I'm now confused about is does this sort of preallocation extent hold any valid data? IIUC it should not, so I
No. Mappings in the cow fork are not fully written and should never be used for reads.
Quoted 3 lines
would expect it to trigger
iomap_block_needs_zeroing()to write zeroes to the folio. Instead, what I see in the issue is that we are trying to do disk read.Hrm. Part of the problem here might be that
iomap_read_folio_rangeignoresiomap_iter::srcmapif it's typeIOMAP_HOLE(seeiomap_iter_srcmap), even if the filesystem actually *set* the srcmap to a hole.FWIW I see a somewhat different failure -- not data corruption, but pwrite returning failure:
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 line 2
Quoted 2 lines
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 renamed from run/fstests/bin/tests/generic/521.out 2025-07-15 14:45:15.100315255 -0700 +1
-0@@ -1,2 +1,668 @@ QA output created by 521 +dowrite: write: Input/output error
Hmm that's strange. Can you try running the above command with prep.fsxops that I've shared. You'll need to pull the fsx atomic write changes in this patch for it to work. I've been running on non atomic write device btw.
Quoted 63 lines
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 renamed from run/fstests/bin/tests/generic/521.out 2025-07-15 14:45:15.100315255 -0700 +41
-0+LOG DUMP (661 total operations): +1( 1 mod 256): TRUNCATE UP from 0x0 to 0x1d000 +2( 2 mod 256): DEDUPE 0x19000 thru 0x1bfff (0x3000 bytes) to 0x13000 thru 0x15fff +3( 3 mod 256): SKIPPED (no operation) +4( 4 mod 256): PUNCH 0x5167 thru 0x12d1c (0xdbb6 bytes) +5( 5 mod 256): WRITE 0x79000 thru 0x86fff (0xe000 bytes) HOLE +6( 6 mod 256): PUNCH 0x32344 thru 0x36faf (0x4c6c bytes) +7( 7 mod 256): READ 0x0 thru 0xfff (0x1000 bytes) +8( 8 mod 256): WRITE 0xe000 thru 0x11fff (0x4000 bytes) +9( 9 mod 256): PUNCH 0x71324 thru 0x86fff (0x15cdc bytes) +10( 10 mod 256): MAPREAD 0x5b000 thru 0x6d218 (0x12219 bytes) +11( 11 mod 256): COLLAPSE 0x70000 thru 0x79fff (0xa000 bytes) +12( 12 mod 256): WRITE 0x41000 thru 0x50fff (0x10000 bytes) +13( 13 mod 256): INSERT 0x39000 thru 0x4dfff (0x15000 bytes) +14( 14 mod 256): WRITE 0x34000 thru 0x37fff (0x4000 bytes) +15( 15 mod 256): MAPREAD 0x55000 thru 0x6ee44 (0x19e45 bytes) +16( 16 mod 256): READ 0x46000 thru 0x55fff (0x10000 bytes) +17( 17 mod 256): PUNCH 0x1ccea thru 0x23b2e (0x6e45 bytes) +18( 18 mod 256): COPY 0x2a000 thru 0x35fff (0xc000 bytes) to 0x52000 thru 0x5dfff +19( 19 mod 256): SKIPPED (no operation) +20( 20 mod 256): WRITE 0x10000 thru 0x1ffff (0x10000 bytes) +645(133 mod 256): READ 0x5000 thru 0x16fff (0x12000 bytes) +646(134 mod 256): PUNCH 0x3a51d thru 0x41978 (0x745c bytes) +647(135 mod 256): FALLOC 0x47f4c thru 0x54867 (0xc91b bytes) INTERIOR +648(136 mod 256): WRITE 0xa000 thru 0x1dfff (0x14000 bytes) +649(137 mod 256): CLONE 0x83000 thru 0x89fff (0x7000 bytes) to 0x4b000 thru 0x51fff +650(138 mod 256): TRUNCATE DOWN from 0x8bac4 to 0x7e000 +651(139 mod 256): MAPWRITE 0x13000 thru 0x170e6 (0x40e7 bytes) +652(140 mod 256): XCHG 0x6a000 thru 0x7cfff (0x13000 bytes) to 0x8000 thru 0x1afff +653(141 mod 256): XCHG 0x35000 thru 0x3cfff (0x8000 bytes) to 0x1b000 thru 0x22fff +654(142 mod 256): CLONE 0x47000 thru 0x60fff (0x1a000 bytes) to 0x65000 thru 0x7efff +655(143 mod 256): DEDUPE 0x79000 thru 0x7dfff (0x5000 bytes) to 0x6e000 thru 0x72fff +656(144 mod 256): XCHG 0x4d000 thru 0x5ffff (0x13000 bytes) to 0x8000 thru 0x1afff +657(145 mod 256): PUNCH 0x7194f thru 0x7efff (0xd6b1 bytes) +658(146 mod 256): PUNCH 0x7af7e thru 0x7efff (0x4082 bytes) +659(147 mod 256): MAPREAD 0x77000 thru 0x7e55d (0x755e bytes) +660(148 mod 256): READ 0x58000 thru 0x64fff (0xd000 bytes) +661(149 mod 256): WRITE 0x88000 thru 0x8bfff (0x4000 bytes) HOLE +Log of operations saved to "/mnt/junk.fsxops"; replay with --replay-ops +Correct content saved for comparison +(maybe hexdump "/mnt/junk" vs "/mnt/junk.fsxgood")Curiously there are no EIO errors logged in dmesg.
--D
Quoted 16 lines
Regards,
ojaswinQuoted 13 lines
Brian
Quoted 8 lines
[1]
https://github.com/OjaswinM/fsx-aw-issue/tree/master[2] https://github.com/OjaswinM/fsx-aw-issue/blob/master/repro.fsxops
regards,
ojaswin
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 11 lines · Ojaswin Mujoo
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 line 2
Quoted 7 lines
FWIW I see a somewhat different failure -- not data corruption, but pwrite returning failure:
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 renamed from run/fstests/bin/tests/generic/521.out 2025-07-15 14:45:15.100315255 -0700 +1
-0@@ -1,2 +1,668 @@ QA output created by 521 +dowrite: write: Input/output errorHmm that's strange. Can you try running the above command with prep.fsxops that I've shared. You'll need to pull the fsx atomic write changes in this patch for it to work. I've been running on non atomic write device btw.
JFYI, I can see this issue with v6.18-rc2 and generic/760
I'll investigate...
Re: [PATCH v7 04/12] ltp/fsx.c: Add atomic writes support to fsx
Quoted 13 lines · John Garry
Quoted 11 lines · Ojaswin Mujoo
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 line 2
Quoted 7 lines
FWIW I see a somewhat different failure -- not data corruption, but pwrite returning failure:
var/tmp/fstests/generic/521.out.bad 2025-10-21 10:33:39.032263811 -0700 renamed from run/fstests/bin/tests/generic/521.out 2025-07-15 14:45:15.100315255 -0700 +1
-0@@ -1,2 +1,668 @@ QA output created by 521 +dowrite: write: Input/output errorHmm that's strange. Can you try running the above command with prep.fsxops that I've shared. You'll need to pull the fsx atomic write changes in this patch for it to work. I've been running on non atomic write device btw.
Quoted 3 lines
JFYI, I can see this issue with v6.18-rc2 and generic/760
I'll investigate...
I figured out why I'm seeing EIO from the ftrace output. Apparently xfs_atomic_write_cow_iomap_begin isn't detecting delalloc extents in the cow fork and converting them to unwritten:
fsx-35984 [003] 1996.389571: xfs_file_direct_write: dev 8:0 ino 0x127 disize 0x88000 pos 0x20000 bytecount 0x10000 fsx-35984 [003] 1996.389572: iomap_dio_rw_begin: dev 8:0 ino 0x127 size 0x88000 offset 0x20000 length 0x10000 done_before 0x0 flags ATOMIC|DIRECT dio_flags aio 0 fsx-35984 [003] 1996.389583: iomap_iter: dev 8:0 ino 0x127 pos 0x20000 length 0x10000 status 0 flags WRITE|DIRECT|ATOMIC (0x211) ops xfs_atomic_write_cow_iomap_ops caller __iomap_dio_rw+0x1cb fsx-35984 [003] 1996.389583: xfs_iomap_atomic_write_cow: dev 8:0 ino 0x127 pos 0x20000 bytecount 0x10000 fsx-35984 [003] 1996.389584: xfs_iomap_found: dev 8:0 ino 0x127 disize 0x88000 pos 0x20000 bytecount 0x10000 fork cow startoff 0x20 startblock 0xffffffffe0007 fsbcount 0x10 fsx-35984 [003] 1996.389584: iomap_iter_dstmap: dev 8:0 ino 0x127 bdev 8:0 addr 0xffffffffffffffff offset 0x20000 length 0x10000 type DELALLOC (0x1) flags DIRTY|SHARED (0x6) fsx-35984 [003] 1996.389595: console: Direct I/O collision with buffered writes! File: /junk Comm: fsx fsx-35984 [003] 1996.391183: iomap_iter: dev 8:0 ino 0x127 pos 0x20000 length 0x10000 status -5 flags WRITE|DIRECT|ATOMIC (0x211) ops xfs_atomic_write_cow_iomap_ops caller __iomap_dio_rw+0x1cb fsx-35984 [003] 1996.391184: iomap_dio_complete: dev 8:0 ino 0x127 size 0x88000 offset 0x20000 flags ATOMIC|DIRECT aio 0 error -5 ret -5
I suspect this needs a xfs_bmapi_convert_delalloc call somewhere around the end of that function.
--D
PATCH 1/1 xfs: fix write failures in software-provided atomic writes
In reply to Ojaswin MujooFrom: Darrick J. Wong <[email protected]>
With the 5 Oct 2025 release of fstests, generic/521 fails for me on regular (aka non-block-atomic-writes) storage:
QA output created by 521
dowrite: write: Input/output error
LOG DUMP (8553 total operations):
1( 1 mod 256): SKIPPED (no operation) 2( 2 mod 256): WRITE 0x7e000 thru 0x8dfff (0x10000 bytes) HOLE 3( 3 mod 256): READ 0x69000 thru 0x79fff (0x11000 bytes) 4( 4 mod 256): FALLOC 0x53c38 thru 0x5e853 (0xac1b bytes) INTERIOR 5( 5 mod 256): COPY 0x55000 thru 0x59fff (0x5000 bytes) to 0x25000 thru 0x29fff 6( 6 mod 256): WRITE 0x74000 thru 0x88fff (0x15000 bytes) 7( 7 mod 256): ZERO 0xedb1 thru 0x11693 (0x28e3 bytes)
<snip>
with a warning in dmesg from iomap about XFS trying to give it a delalloc mapping for a directio write. Fix the software atomic write iomap_begin code to convert the reservation into a written mapping. This doesn't fix the data corruption problems reported by generic/760, but it's a start.
--- fs/xfs/xfs_iomap.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
fs/xfs/xfs_iomap.c +19 -2
@@ -1130,7 +1130,7 @@ xfs_atomic_write_cow_iomap_begin(return -EAGAIN ;trace_xfs_iomap_atomic_write_cow (ip, offset, length); - +retry:xfs_ilock (ip,XFS_ILOCK_EXCL );if (!ip->i_cowfp) {
@@ -1141,6 +1141,8 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { +if (isnullstartblock (cmap.br_startblock)) + goto convert;xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }
@@ -1169,8 +1171,10 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { -xfs_trim_extent (&cmap, offset_fsb, count_fsb);xfs_trans_cancel (tp); +if (isnullstartblock (cmap.br_startblock)) + goto convert; +xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }
@@ -1210,6 +1214,19 @@ xfs_atomic_write_cow_iomap_begin(xfs_iunlock (ip,XFS_ILOCK_EXCL );return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq); +convert: +xfs_iunlock (ip,XFS_ILOCK_EXCL ); + error =xfs_bmapi_convert_delalloc (ip,XFS_COW_FORK , offset, iomap, +NULL ); +if (error) +return error; + +/* +* Try the lookup again, because the delalloc conversion might have +* turned the COW mapping into unwritten, but we need it to be in +* written state. +*/ + goto retry; out_unlock:xfs_iunlock (ip,XFS_ILOCK_EXCL );return error;
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 15 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
With the 5 Oct 2025 release of fstests, generic/521 fails for me on regular (aka non-block-atomic-writes) storage:
QA output created by 521
dowrite: write: Input/output error
LOG DUMP (8553 total operations):1( 1 mod 256): SKIPPED (no operation) 2( 2 mod 256): WRITE 0x7e000 thru 0x8dfff (0x10000 bytes) HOLE 3( 3 mod 256): READ 0x69000 thru 0x79fff (0x11000 bytes) 4( 4 mod 256): FALLOC 0x53c38 thru 0x5e853 (0xac1b bytes) INTERIOR 5( 5 mod 256): COPY 0x55000 thru 0x59fff (0x5000 bytes) to 0x25000 thru 0x29fff 6( 6 mod 256): WRITE 0x74000 thru 0x88fff (0x15000 bytes) 7( 7 mod 256): ZERO 0xedb1 thru 0x11693 (0x28e3 bytes)
Quoted 7 lines
<snip>
with a warning in dmesg from iomap about XFS trying to give it a delalloc mapping for a directio write. Fix the software atomic write
iomap_begincode to convert the reservation into a written mapping. This doesn't fix the data corruption problems reported by generic/760, but it's a start.
Grrrr I forgot to remove the reply-to tag when I harvested the cc list from this old thread for a bug fix. Sorry about that.
--D
Quoted 63 lines
Cc <[email protected]> # v6.16Fixes bd1d2c21d5d249 ("xfs: add xfs_atomic_write_cow_iomap_begin()")Signed-off-by "Darrick J. Wong" <[email protected]>--- fs/xfs/xfs_iomap.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)fs/xfs/xfs_iomap.c +19
-2@@ -1130,7 +1130,7 @@ xfs_atomic_write_cow_iomap_begin(return -EAGAIN ;trace_xfs_iomap_atomic_write_cow (ip, offset, length); - +retry:xfs_ilock (ip,XFS_ILOCK_EXCL );if (!ip->i_cowfp) {@@ -1141,6 +1141,8 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { +if (isnullstartblock (cmap.br_startblock)) + goto convert;xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }@@ -1169,8 +1171,10 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { -xfs_trim_extent (&cmap, offset_fsb, count_fsb);xfs_trans_cancel (tp); +if (isnullstartblock (cmap.br_startblock)) + goto convert; +xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }@@ -1210,6 +1214,19 @@ xfs_atomic_write_cow_iomap_begin(xfs_iunlock (ip,XFS_ILOCK_EXCL );return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq); +convert: +xfs_iunlock (ip,XFS_ILOCK_EXCL ); + error =xfs_bmapi_convert_delalloc (ip,XFS_COW_FORK , offset, iomap, +NULL ); +if (error) +return error; + +/* +* Try the lookup again, because the delalloc conversion might have +* turned the COW mapping into unwritten, but we need it to be in +* written state. +*/ + goto retry; out_unlock:xfs_iunlock (ip,XFS_ILOCK_EXCL );return error;
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
In reply to Darrick J. WongQuoted 15 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
With the 5 Oct 2025 release of fstests, generic/521 fails for me on regular (aka non-block-atomic-writes) storage:
QA output created by 521
dowrite: write: Input/output error
LOG DUMP (8553 total operations):1( 1 mod 256): SKIPPED (no operation) 2( 2 mod 256): WRITE 0x7e000 thru 0x8dfff (0x10000 bytes) HOLE 3( 3 mod 256): READ 0x69000 thru 0x79fff (0x11000 bytes) 4( 4 mod 256): FALLOC 0x53c38 thru 0x5e853 (0xac1b bytes) INTERIOR 5( 5 mod 256): COPY 0x55000 thru 0x59fff (0x5000 bytes) to 0x25000 thru 0x29fff 6( 6 mod 256): WRITE 0x74000 thru 0x88fff (0x15000 bytes) 7( 7 mod 256): ZERO 0xedb1 thru 0x11693 (0x28e3 bytes)
Quoted 7 lines
<snip>
with a warning in dmesg from iomap about XFS trying to give it a delalloc mapping for a directio write. Fix the software atomic write
iomap_begincode to convert the reservation into a written mapping. This doesn't fix the data corruption problems reported by generic/760, but it's a start.
I was seeing the corruption and, as expected, unfortunately this does not fix the issue. Indeed, I don't even touch the new codepath when testing (for that corruption).
As for that corruption, I am seeing the same behaviour as Ojaswin described. The failure is in a read operation.
It seems to be a special combo of atomic write, write, and then read which reliably shows the issue. The regular write seems to write to the cow fork, so I am guessing that the atomic write does not leave it in proper state.
I do notice for the atomic write that we are writing (calling xfs_atomic_write_cow_iomap_begin() -> xfs_bmapi_write()) for more blocks that are required for the atomic write. The regular write overwrites these blocks, and the read is corrupted in the blocks just after the atomic write. It's as if the blocks just after atomic write are not left in the proper state.
Quoted 62 lines
Cc <[email protected]> # v6.16Fixes bd1d2c21d5d249 ("xfs: add xfs_atomic_write_cow_iomap_begin()")Signed-off-by "Darrick J. Wong" <[email protected]>--- fs/xfs/xfs_iomap.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)fs/xfs/xfs_iomap.c +19
-2@@ -1130,7 +1130,7 @@ xfs_atomic_write_cow_iomap_begin(return -EAGAIN ;trace_xfs_iomap_atomic_write_cow (ip, offset, length); - +retry:xfs_ilock (ip,XFS_ILOCK_EXCL );if (!ip->i_cowfp) {@@ -1141,6 +1141,8 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { +if (isnullstartblock (cmap.br_startblock)) + goto convert;xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }@@ -1169,8 +1171,10 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { -xfs_trim_extent (&cmap, offset_fsb, count_fsb);xfs_trans_cancel (tp); +if (isnullstartblock (cmap.br_startblock)) + goto convert; +xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }@@ -1210,6 +1214,19 @@ xfs_atomic_write_cow_iomap_begin(xfs_iunlock (ip,XFS_ILOCK_EXCL );return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq); +convert: +xfs_iunlock (ip,XFS_ILOCK_EXCL ); + error =xfs_bmapi_convert_delalloc (ip,XFS_COW_FORK , offset, iomap, +NULL ); +if (error) +return error; + +/* +* Try the lookup again, because the delalloc conversion might have +* turned the COW mapping into unwritten, but we need it to be in +* written state. +*/ + goto retry; out_unlock:xfs_iunlock (ip,XFS_ILOCK_EXCL );return error;
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 24 lines · John Garry
Quoted 15 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
With the 5 Oct 2025 release of fstests, generic/521 fails for me on regular (aka non-block-atomic-writes) storage:
QA output created by 521
dowrite: write: Input/output error
LOG DUMP (8553 total operations):1( 1 mod 256): SKIPPED (no operation) 2( 2 mod 256): WRITE 0x7e000 thru 0x8dfff (0x10000 bytes) HOLE 3( 3 mod 256): READ 0x69000 thru 0x79fff (0x11000 bytes) 4( 4 mod 256): FALLOC 0x53c38 thru 0x5e853 (0xac1b bytes) INTERIOR 5( 5 mod 256): COPY 0x55000 thru 0x59fff (0x5000 bytes) to 0x25000 thru 0x29fff 6( 6 mod 256): WRITE 0x74000 thru 0x88fff (0x15000 bytes) 7( 7 mod 256): ZERO 0xedb1 thru 0x11693 (0x28e3 bytes)Quoted 7 lines
<snip>
with a warning in dmesg from iomap about XFS trying to give it a delalloc mapping for a directio write. Fix the software atomic write
iomap_begincode to convert the reservation into a written mapping. This doesn't fix the data corruption problems reported by generic/760, but it's a start.
Quoted 3 lines
I was seeing the corruption and, as expected, unfortunately this does not fix the issue. Indeed, I don't even touch the new codepath when testing (for that corruption).
Yeah, I know. This fix enables me to move on to what I think is the corruption that you and Ojaswin are seeing.
Quoted 7 lines
As for that corruption, I am seeing the same behaviour as Ojaswin described. The failure is in a read operation.
It seems to be a special combo of atomic write, write, and then read which reliably shows the issue. The regular write seems to write to the cow fork, so I am guessing that the atomic write does not leave it in proper state.
Quoted 6 lines
I do notice for the atomic write that we are writing (calling
xfs_atomic_write_cow_iomap_begin()->xfs_bmapi_write()) for more blocks that are required for the atomic write. The regular write overwrites these blocks, and the read is corrupted in the blocks just after the atomic write. It's as if the blocks just after atomic write are not left in the proper state.
That's a good breadcrumb for me to follow; I will turn on the rmap tracepoints to see if they give me a better idea of what's going on. I mentioned earlier that I think the problem could be that iomap treats srcmap::type == IOMAP_HOLE as if the srcmap isn't there, and so it'll read from the cow fork blocks even though that's not right.
--D
Quoted 59 lines
Quoted 58 lines
Cc <[email protected]> # v6.16Fixes bd1d2c21d5d249 ("xfs: add xfs_atomic_write_cow_iomap_begin()")Signed-off-by "Darrick J. Wong" <[email protected]>--- fs/xfs/xfs_iomap.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)fs/xfs/xfs_iomap.c +19
-2@@ -1130,7 +1130,7 @@ xfs_atomic_write_cow_iomap_begin(return -EAGAIN ;trace_xfs_iomap_atomic_write_cow (ip, offset, length); - +retry:xfs_ilock (ip,XFS_ILOCK_EXCL );if (!ip->i_cowfp) {@@ -1141,6 +1141,8 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { +if (isnullstartblock (cmap.br_startblock)) + goto convert;xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }@@ -1169,8 +1171,10 @@ xfs_atomic_write_cow_iomap_begin(if (!xfs_iext_lookup_extent (ip, ip->i_cowfp, offset_fsb, &icur, &cmap)) cmap.br_startoff = end_fsb;if (cmap.br_startoff <= offset_fsb) { -xfs_trim_extent (&cmap, offset_fsb, count_fsb);xfs_trans_cancel (tp); +if (isnullstartblock (cmap.br_startblock)) + goto convert; +xfs_trim_extent (&cmap, offset_fsb, count_fsb); goto found; }@@ -1210,6 +1214,19 @@ xfs_atomic_write_cow_iomap_begin(xfs_iunlock (ip,XFS_ILOCK_EXCL );return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq); +convert: +xfs_iunlock (ip,XFS_ILOCK_EXCL ); + error =xfs_bmapi_convert_delalloc (ip,XFS_COW_FORK , offset, iomap, +NULL ); +if (error) +return error; + +/* +* Try the lookup again, because the delalloc conversion might have +* turned the COW mapping into unwritten, but we need it to be in +* written state. +*/ + goto retry; out_unlock:xfs_iunlock (ip,XFS_ILOCK_EXCL );return error;
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 7 lines · Darrick J. Wong
Quoted 7 lines
As for that corruption, I am seeing the same behaviour as Ojaswin described. The failure is in a read operation.
It seems to be a special combo of atomic write, write, and then read which reliably shows the issue. The regular write seems to write to the cow fork, so I am guessing that the atomic write does not leave it in proper state.
Quoted 7 lines
Quoted 6 lines
I do notice for the atomic write that we are writing (calling
xfs_atomic_write_cow_iomap_begin()->xfs_bmapi_write()) for more blocks that are required for the atomic write. The regular write overwrites these blocks, and the read is corrupted in the blocks just after the atomic write. It's as if the blocks just after atomic write are not left in the proper state.That's a good breadcrumb for me to follow;
I hope that it is ...
Quoted 5 lines
I will turn on the rmap
tracepoints to see if they give me a better idea of what's going on. I mentioned earlier that I think the problem could be that iomap treatssrcmap::type==IOMAP_HOLEas if the srcmap isn't there, and so it'll read from the cow fork blocks even though that's not right.
Something else I notice for my failing test is that we do the regular write, it ends in a sub-fs block write on a hole. But that fs block (which was part of a hole) ends up being filled with all the same data pattern (when I would expect the unwritten region to be 0s when read back) - and this is what the compare fails on.
cheers
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 5 lines · John Garry
Quoted 2 lines
Quoted 1 line
That's a good breadcrumb for me to follow;
I hope that it is ...
Quoted 7 lines
Quoted 5 lines
I will turn on the rmap
tracepoints to see if they give me a better idea of what's going on. I mentioned earlier that I think the problem could be that iomap treatssrcmap::type==IOMAP_HOLEas if the srcmap isn't there, and so it'll read from the cow fork blocks even though that's not right.Something else I notice for my failing test is that we do the regular write, it ends in a sub-fs block write on a hole. But that fs block (which was part of a hole) ends up being filled with all the same data pattern (when I would expect the unwritten region to be 0s when read back) - and this is what the compare fails on.
This makes the problem go away for me:
fs/xfs/xfs_iomap.c +2 -1
@@ -1113,6 +1113,7 @@ xfs_atomic_write_cow_iomap_begin(unsigned int dblocks =0 , rblocks =0 ;int error;u64 seq; +xfs_filblks_t count_fsb_orig = count_fsb;ASSERT (flags &IOMAP_WRITE );ASSERT (flags &IOMAP_DIRECT );
@@ -1202,7 +1203,7 @@ xfs_atomic_write_cow_iomap_begin( found:if (cmap.br_state !=XFS_EXT_NORM ) { error =xfs_reflink_convert_cow_locked (ip, offset_fsb, - count_fsb); + count_fsb_orig);if (error) goto out_unlock; cmap.br_state =XFS_EXT_NORM ;
@@ -1215,6 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq);
I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
thanks
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 44 lines · John Garry
Quoted 11 lines · John Garry
Quoted 2 lines
Quoted 1 line
That's a good breadcrumb for me to follow;
I hope that it is ...
Quoted 5 lines
I will turn on the rmap
tracepoints to see if they give me a better idea of what's going on. I mentioned earlier that I think the problem could be that iomap treatssrcmap::type==IOMAP_HOLEas if the srcmap isn't there, and so it'll read from the cow fork blocks even though that's not right.Quoted 5 lines
Something else I notice for my failing test is that we do the regular write, it ends in a sub-fs block write on a hole. But that fs block (which was part of a hole) ends up being filled with all the same data pattern (when I would expect the unwritten region to be 0s when read back) - and this is what the compare fails on.
This makes the problem go away for me:
fs/xfs/xfs_iomap.c +2
-1@@ -1113,6 +1113,7 @@ xfs_atomic_write_cow_iomap_begin(unsigned int dblocks =0 , rblocks =0 ;int error;u64 seq; +xfs_filblks_t count_fsb_orig = count_fsb;ASSERT (flags &IOMAP_WRITE );ASSERT (flags &IOMAP_DIRECT );@@ -1202,7 +1203,7 @@ xfs_atomic_write_cow_iomap_begin( found:if (cmap.br_state !=XFS_EXT_NORM ) { error =xfs_reflink_convert_cow_locked (ip, offset_fsb, - count_fsb); + count_fsb_orig);if (error) goto out_unlock; cmap.br_state =XFS_EXT_NORM ;@@ -1215,6 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq);
Quoted 3 lines
I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
That looks like a good correction to me; I'll run that on my test fleet overnight and we'll see what happens. Thanks for putting this together!
--D
Quoted 2 lines
thanks
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 8 lines · Darrick J. Wong
Quoted 6 lines
fs/xfs/xfs_iomap.c +0
-0@@ -1215,6 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq);I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
That looks like a good correction to me; I'll run that on my test fleet overnight and we'll see what happens. Thanks for putting this together!
Cool, but I am not confident that it is a completely correct. Here's the updated code:
int error;
u64 seq;
+ xfs_filblks_t count_fsb_orig = count_fsb;
ASSERT(flags & IOMAP_WRITE);
ASSERT(flags & IOMAP_DIRECT);
@@ -1202,7 +1203,7 @@ xfs_atomic_write_cow_iomap_begin(
found:
if (cmap.br_state != XFS_EXT_NORM) {
error = xfs_reflink_convert_cow_locked(ip, offset_fsb,
- count_fsb);
+ count_fsb_orig);
if (error)
goto out_unlock;
cmap.br_state = XFS_EXT_NORM;
cmap may be longer than count_fsb_orig (which was the failing scenario). In that case, after calling xfs_reflink_convert_cow_locked(), we would have partially converted cmap, so it is proper to set cmap.br_state = XFS_EXT_NORM? We should trim cmap to count_fsb_orig also, right?
I don't think that it makes much of a difference, but it seems the proper thing to do. Maybe the subsequent traces length values would be inconsistent with other path to @found label if we don't trim.
Thank,
John
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 20 lines · John Garry
Quoted 8 lines · Darrick J. Wong
Quoted 6 lines
fs/xfs/xfs_iomap.c +0
-0@@ -1215,6 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq);I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
That looks like a good correction to me; I'll run that on my test fleet overnight and we'll see what happens. Thanks for putting this together!
Cool, but I am not confident that it is a completely correct. Here's the updated code:
fs/xfs/xfs_iomap.c +1
-0int error;u64 seq; +xfs_filblks_t count_fsb_orig = count_fsb;ASSERT (flags &IOMAP_WRITE );ASSERT (flags &IOMAP_DIRECT );
fs/xfs/xfs_iomap.c xfs_atomic_write_cow_iomap_begin( · line 1209
Quoted 8 lines
fs/xfs/xfs_iomap.c +1
-1@@ -1202,7 +1203,7 @@ xfs_atomic_write_cow_iomap_begin( found:if (cmap.br_state !=XFS_EXT_NORM ) { error =xfs_reflink_convert_cow_locked (ip, offset_fsb, - count_fsb); + count_fsb_orig);if (error) goto out_unlock; cmap.br_state =XFS_EXT_NORM ;
Er... this is exactly the same snippet as yesterday. <confused>
(That snippet seems to have survived overnight fsx)
Quoted 4 lines
cmap may be longer than
count_fsb_orig(which was the failing scenario). In that case, after callingxfs_reflink_convert_cow_locked(), we would have partially converted cmap, so it is proper to set cmap.br_state =XFS_EXT_NORM?
Hrm. Let me walk through this function again.
At the start, {offset,count}_fsb express the offset/length parameters, but in fsblock units and expanded to align with an fsblock.
If the cow fork has a mapping (cmap) that starts before the offset, we trim the mapping to the desired range and goto found. cmap cannot end before offset_fsb because that's how xfs_iext_lookup_extent works.
If the cow fork doesn't have a mapping or the one it does have doesn't start until after offset_fsb, then we trim count_fsb so that (offset_fsb, count_fsb) is the range that isn't mapped to space.
Then we cycle the ILOCK to get a transaction and do the lookup again due to "extent layout could have changed since the unlock". Same rules as the first lookup. I wonder if that second xfs_trim_extent should be using orig_count_fsb, because at this point it's trimming the cmap to the shorter range. It's probably not a big deal because iomap will just call ->iomap_begin on the rest of the range, but it's more work.
If the second lookup doesn't produce a mapping, then we call xfs_bmapi_write to fill the hole and drop down to @found.
Now, we're at the found: label, having arrived here in one of three ways:
1) The first cmap lookup found at least one block that it can use. (offset_fsb, count_fsb) is the original range that iomap asked for. This mapping could be written or unwritten, and it's been trimmed so that it won't extend outside the requested write range.
2) The second cmap lookup found at least one block that it can use. (offset_fsb, count_fsb) is a truncated range because the cow fork has a mapping that starts at (offset_fsb + count_fsb). This mapping could also be written or unwritten, and it also has been trimmed so that it won't extend outside the hole range.
Why do we trim cmap to the hole range? The original write range will suite iomap just fine.
3) We xfs_bmapi_write'd a hole, and now we have an unwritten mapping. (offset_fsb, count_fsb) is the same truncated range from 2). cmap is potentially much larger than (offset_fsb, count_fsb).
Why do we not trim this mapping to the original write range?
If at this point the mapping is unwritten, we convert it to written mapping with xfs_reflink_convert_cow_locked. offset_fsb retains its original value, but what is count_fsb? It's either the original value
(1) or the smaller one from filling the hole (2) or (3)? Why don't we pass the cmap startoff/blockcount to this function?
As an aside: Changing count_fsb makes it harder for me to understand what's going on in this function.
Now, having converted either the range we arrived at via (1-3) (or with your patch, the original range) to written state, we set br_state and pass that back to iomap. I think in case (3) it's possible that xfs incorrectly reports to iomap an overly large mapping that might not actually reflect the cow fork contents, because we only converted so much of the mapping state.
Quoted 1 line
We should trim cmap to
count_fsb_origalso, right?
iomap doesn't care if the mapping it receives spans more space than just the range it asked for, but XFS shouldn't be exposing mappings that don't reflect the actual state of the cow fork.
I think there are several things wrong with this function:
A) xfs_bmapi_write can return a much larger unwritten mapping than what the caller asked for. We convert part of that range to written, but return the entire written mapping to iomap even though that's inaccurate.
B) The arguments to xfs_reflink_convert_cow_locked are wrong -- an unwritten mapping could be *smaller* than the write range (or even the hole range). In this case, we convert too much file range to written state because we then return a smaller mapping to iomap.
C) It doesn't handle delalloc mappings. This I covered in the patch that I already sent to the list.
D) Reassigning count_fsb to handle the hole means that if the second cmap lookup attempt succeeds (due to racing with someone else) we trim the mapping more than is strictly necessary. The changing meaning of count_fsb makes this harder to notice.
E) The tracepoint is kinda wrong because @length is mutated. That makes it harder to chase the data flows through this function because you can't just grep on the pos/bytecount strings.
F) We don't actually check that the br_state = XFS_EXT_NORM assignment is accurate, i.e. that the cow fork contains a written mapping for the range that we're interested in.
G) Somewhat inadequate documentation of why we need to xfs_trim_extent so aggressively in this function.
H) Not sure why xfs_iomap_end_fsb is used here, the vfs already clamped the write range to s_maxbytes.
Quoted 3 lines
I don't think that it makes much of a difference, but it seems the proper thing to do. Maybe the subsequent traces length values would be inconsistent with other path to @found label if we don't trim.
With A-H fixed, all the atomic writes issues I was seeing went away. I'll run this through the atomic writes QA tests and send a fix series to the list.
--D
Quoted 5 lines
Thank,
John
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 18 lines · Darrick J. Wong
Quoted 17 lines · John Garry
Quoted 8 lines · Darrick J. Wong
Quoted 6 lines
fs/xfs/xfs_iomap.c +0
-0@@ -1215,6 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq);I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
That looks like a good correction to me; I'll run that on my test fleet overnight and we'll see what happens. Thanks for putting this together!
Cool, but I am not confident that it is a completely correct. Here's the updated code:
int error;
u64 seq;fs/xfs/xfs_iomap.c +1
-0+xfs_filblks_t count_fsb_orig = count_fsb;
Quoted 13 lines
fs/xfs/xfs_iomap.c xfs_atomic_write_cow_iomap_begin( · line 1209
Quoted 11 lines
ASSERT(flags & IOMAP_WRITE); ASSERT(flags & IOMAP_DIRECT);fs/xfs/xfs_iomap.c +1
-1@@ -1202,7 +1203,7 @@ xfs_atomic_write_cow_iomap_begin( found:if (cmap.br_state !=XFS_EXT_NORM ) { error =xfs_reflink_convert_cow_locked (ip, offset_fsb, - count_fsb); + count_fsb_orig);if (error) goto out_unlock; cmap.br_state =XFS_EXT_NORM ;Er... this is exactly the same snippet as yesterday. <confused>
Yes, I was just showing it for context.
Quoted 8 lines
(That snippet seems to have survived overnight fsx)
Quoted 4 lines
cmap may be longer than
count_fsb_orig(which was the failing scenario). In that case, after callingxfs_reflink_convert_cow_locked(), we would have partially converted cmap, so it is proper to set cmap.br_state =XFS_EXT_NORM?
Quoted 4 lines
Hrm. Let me walk through this function again.
At the start, {offset,count}_fsb express the offset/length parameters, but in fsblock units and expanded to align with an fsblock.
I do also note that the usage of xfs_iomap_end_fsb() is a bit dubious, as this will truncate the write if it goes over s_maxbytes. However, we never want to truncate an atomic write.
Quoted 9 lines
If the cow fork has a mapping (cmap) that starts before the offset, we trim the mapping to the desired range and goto found. cmap cannot end before
offset_fsbbecause that's howxfs_iext_lookup_extentworks.If the cow fork doesn't have a mapping or the one it does have doesn't start until after
offset_fsb, then we trimcount_fsbso that (offset_fsb,count_fsb) is the range that isn't mapped to space.
Quoted 5 lines
Then we cycle the ILOCK to get a transaction and do the lookup again due to "extent layout could have changed since the unlock". Same rules as the first lookup. I wonder if that second
xfs_trim_extentshould be usingorig_count_fsb, because at this point it's trimming the cmap to the shorter range.
Yes, I think so.
Quoted 14 lines
It's probably not a big deal because iomap will
just call ->iomap_begin on the rest of the range, but it's more work.If the second lookup doesn't produce a mapping, then we call
xfs_bmapi_writeto fill the hole and drop down to @found.Now, we're at the found: label, having arrived here in one of three ways:
1) The first cmap lookup found at least one block that it can use. (
offset_fsb,count_fsb) is the original range that iomap asked for. This mapping could be written or unwritten, and it's been trimmed so that it won't extend outside the requested write range.
Quoted 8 lines
2) The second cmap lookup found at least one block that it can use. (
offset_fsb,count_fsb) is a truncated range because the cow fork has a mapping that starts at (offset_fsb+count_fsb). This mapping could also be written or unwritten, and it also has been trimmed so that it won't extend outside the hole range.Why do we trim cmap to the hole range? The original write range will suite iomap just fine.
I think that the xfs_iext_lookup_extent() and xfs_trim_extent() pattern may have been just copied without considering this.
Quoted 7 lines
3) We xfs_bmapi_write'd a hole, and now we have an unwritten mapping. (
offset_fsb,count_fsb) is the same truncated range from 2). cmap is potentially much larger than (offset_fsb,count_fsb).Why do we not trim this mapping to the original write range?
I don't know, but this is what I was suggesting should happen.
Quoted 5 lines
If at this point the mapping is unwritten, we convert it to written mapping with
xfs_reflink_convert_cow_locked.offset_fsbretains its original value, but what iscount_fsb? It's either the original value
(1) or the smaller one from filling the hole (2) or (3)? Why don't we pass the cmap startoff/blockcount to this function?
I think that we should
Quoted 32 lines
As an aside: Changing
count_fsbmakes it harder for me to understand what's going on in this function.Now, having converted either the range we arrived at via (1-3) (or with your patch, the original range) to written state, we set
br_stateand pass that back to iomap. I think in case (3) it's possible that xfs incorrectly reports to iomap an overly large mapping that might not actually reflect the cow fork contents, because we only converted so much of the mapping state.Quoted 1 line
We should trim cmap to
count_fsb_origalso, right?iomap doesn't care if the mapping it receives spans more space than just the range it asked for, but XFS shouldn't be exposing mappings that don't reflect the actual state of the cow fork.
I think there are several things wrong with this function:
A)
xfs_bmapi_writecan return a much larger unwritten mapping than what the caller asked for. We convert part of that range to written, but return the entire written mapping to iomap even though that's inaccurate.B) The arguments to
xfs_reflink_convert_cow_lockedare wrong -- an unwritten mapping could be *smaller* than the write range (or even the hole range). In this case, we convert too much file range to written state because we then return a smaller mapping to iomap.C) It doesn't handle delalloc mappings. This I covered in the patch that I already sent to the list.
Quoted 8 lines
D) Reassigning
count_fsbto handle the hole means that if the second cmap lookup attempt succeeds (due to racing with someone else) we trim the mapping more than is strictly necessary. The changing meaning ofcount_fsbmakes this harder to notice.E) The tracepoint is kinda wrong because @length is mutated. That makes it harder to chase the data flows through this function because you can't just grep on the pos/bytecount strings.
Yes, I was noticing this also.
Quoted 10 lines
F) We don't actually check that the
br_state=XFS_EXT_NORMassignment is accurate, i.e. that the cow fork contains a written mapping for the range that we're interested in.G) Somewhat inadequate documentation of why we need to
xfs_trim_extentso aggressively in this function.H) Not sure why
xfs_iomap_end_fsbis used here, the vfs already clamped the write range tos_maxbytes.
Please note that I mentioned about this above.
Quoted 8 lines
Quoted 4 lines
I don't think that it makes much of a difference, but it seems the proper thing to do. Maybe the subsequent traces length values would be inconsistent with other path to @found label if we don't trim.
With A-H fixed, all the atomic writes issues I was seeing went away. I'll run this through the atomic writes QA tests and send a fix series to the list.
ok, thanks!
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 174 lines · John Garry
Quoted 18 lines · Darrick J. Wong
Quoted 17 lines · John Garry
Quoted 8 lines · Darrick J. Wong
Quoted 6 lines
fs/xfs/xfs_iomap.c +0
-0@@ -1215,6 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq);I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
That looks like a good correction to me; I'll run that on my test fleet overnight and we'll see what happens. Thanks for putting this together!
Cool, but I am not confident that it is a completely correct. Here's the updated code:
int error;
u64 seq;fs/xfs/xfs_iomap.c +1
-0+xfs_filblks_t count_fsb_orig = count_fsb;Quoted 13 lines
fs/xfs/xfs_iomap.c xfs_atomic_write_cow_iomap_begin( · line 1209
Quoted 11 lines
ASSERT(flags & IOMAP_WRITE); ASSERT(flags & IOMAP_DIRECT);fs/xfs/xfs_iomap.c +1
-1@@ -1202,7 +1203,7 @@ xfs_atomic_write_cow_iomap_begin( found:if (cmap.br_state !=XFS_EXT_NORM ) { error =xfs_reflink_convert_cow_locked (ip, offset_fsb, - count_fsb); + count_fsb_orig);if (error) goto out_unlock; cmap.br_state =XFS_EXT_NORM ;Er... this is exactly the same snippet as yesterday. <confused>
Yes, I was just showing it for context.
Quoted 8 lines
(That snippet seems to have survived overnight fsx)
Quoted 4 lines
cmap may be longer than
count_fsb_orig(which was the failing scenario). In that case, after callingxfs_reflink_convert_cow_locked(), we would have partially converted cmap, so it is proper to set cmap.br_state =XFS_EXT_NORM?Quoted 4 lines
Hrm. Let me walk through this function again.
At the start, {offset,count}_fsb express the offset/length parameters, but in fsblock units and expanded to align with an fsblock.
I do also note that the usage of
xfs_iomap_end_fsb()is a bit dubious, as this will truncate the write if it goes overs_maxbytes. However, we never want to truncate an atomic write.Quoted 9 lines
If the cow fork has a mapping (cmap) that starts before the offset, we trim the mapping to the desired range and goto found. cmap cannot end before
offset_fsbbecause that's howxfs_iext_lookup_extentworks.If the cow fork doesn't have a mapping or the one it does have doesn't start until after
offset_fsb, then we trimcount_fsbso that (offset_fsb,count_fsb) is the range that isn't mapped to space.Quoted 5 lines
Then we cycle the ILOCK to get a transaction and do the lookup again due to "extent layout could have changed since the unlock". Same rules as the first lookup. I wonder if that second
xfs_trim_extentshould be usingorig_count_fsb, because at this point it's trimming the cmap to the shorter range.Yes, I think so.
Quoted 14 lines
It's probably not a big deal because iomap will
just call ->iomap_begin on the rest of the range, but it's more work.If the second lookup doesn't produce a mapping, then we call
xfs_bmapi_writeto fill the hole and drop down to @found.Now, we're at the found: label, having arrived here in one of three ways:
1) The first cmap lookup found at least one block that it can use. (
offset_fsb,count_fsb) is the original range that iomap asked for. This mapping could be written or unwritten, and it's been trimmed so that it won't extend outside the requested write range.Quoted 8 lines
2) The second cmap lookup found at least one block that it can use. (
offset_fsb,count_fsb) is a truncated range because the cow fork has a mapping that starts at (offset_fsb+count_fsb). This mapping could also be written or unwritten, and it also has been trimmed so that it won't extend outside the hole range.Why do we trim cmap to the hole range? The original write range will suite iomap just fine.
I think that the
xfs_iext_lookup_extent()andxfs_trim_extent()pattern may have been just copied without considering this.Quoted 7 lines
3) We xfs_bmapi_write'd a hole, and now we have an unwritten mapping. (
offset_fsb,count_fsb) is the same truncated range from 2). cmap is potentially much larger than (offset_fsb,count_fsb).Why do we not trim this mapping to the original write range?
I don't know, but this is what I was suggesting should happen.
Quoted 5 lines
If at this point the mapping is unwritten, we convert it to written mapping with
xfs_reflink_convert_cow_locked.offset_fsbretains its original value, but what iscount_fsb? It's either the original value
(1) or the smaller one from filling the hole (2) or (3)? Why don't we pass the cmap startoff/blockcount to this function?I think that we should
Quoted 32 lines
As an aside: Changing
count_fsbmakes it harder for me to understand what's going on in this function.Now, having converted either the range we arrived at via (1-3) (or with your patch, the original range) to written state, we set
br_stateand pass that back to iomap. I think in case (3) it's possible that xfs incorrectly reports to iomap an overly large mapping that might not actually reflect the cow fork contents, because we only converted so much of the mapping state.Quoted 1 line
We should trim cmap to
count_fsb_origalso, right?iomap doesn't care if the mapping it receives spans more space than just the range it asked for, but XFS shouldn't be exposing mappings that don't reflect the actual state of the cow fork.
I think there are several things wrong with this function:
A)
xfs_bmapi_writecan return a much larger unwritten mapping than what the caller asked for. We convert part of that range to written, but return the entire written mapping to iomap even though that's inaccurate.B) The arguments to
xfs_reflink_convert_cow_lockedare wrong -- an unwritten mapping could be *smaller* than the write range (or even the hole range). In this case, we convert too much file range to written state because we then return a smaller mapping to iomap.C) It doesn't handle delalloc mappings. This I covered in the patch that I already sent to the list.
Quoted 8 lines
D) Reassigning
count_fsbto handle the hole means that if the second cmap lookup attempt succeeds (due to racing with someone else) we trim the mapping more than is strictly necessary. The changing meaning ofcount_fsbmakes this harder to notice.E) The tracepoint is kinda wrong because @length is mutated. That makes it harder to chase the data flows through this function because you can't just grep on the pos/bytecount strings.
Yes, I was noticing this also.
Quoted 10 lines
F) We don't actually check that the
br_state=XFS_EXT_NORMassignment is accurate, i.e. that the cow fork contains a written mapping for the range that we're interested in.G) Somewhat inadequate documentation of why we need to
xfs_trim_extentso aggressively in this function.H) Not sure why
xfs_iomap_end_fsbis used here, the vfs already clamped the write range tos_maxbytes.Please note that I mentioned about this above.
Quoted 5 lines
Quoted 4 lines
I don't think that it makes much of a difference, but it seems the proper thing to do. Maybe the subsequent traces length values would be inconsistent with other path to @found label if we don't trim.
Quoted 5 lines
Quoted 3 lines
With A-H fixed, all the atomic writes issues I was seeing went away. I'll run this through the atomic writes QA tests and send a fix series to the list.
ok, thanks!
<nod> Fixes have now been sent to the list.
--D
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
In reply to John GarryQuoted 44 lines · John Garry
Quoted 11 lines · John Garry
Quoted 2 lines
Quoted 1 line
That's a good breadcrumb for me to follow;
I hope that it is ...
Quoted 5 lines
I will turn on the rmap
tracepoints to see if they give me a better idea of what's going on. I mentioned earlier that I think the problem could be that iomap treatssrcmap::type==IOMAP_HOLEas if the srcmap isn't there, and so it'll read from the cow fork blocks even though that's not right.Quoted 5 lines
Something else I notice for my failing test is that we do the regular write, it ends in a sub-fs block write on a hole. But that fs block (which was part of a hole) ends up being filled with all the same data pattern (when I would expect the unwritten region to be 0s when read back) - and this is what the compare fails on.
This makes the problem go away for me:
fs/xfs/xfs_iomap.c +2
-1@@ -1113,6 +1113,7 @@ xfs_atomic_write_cow_iomap_begin(unsigned int dblocks =0 , rblocks =0 ;int error;u64 seq; +xfs_filblks_t count_fsb_orig = count_fsb;ASSERT (flags &IOMAP_WRITE );ASSERT (flags &IOMAP_DIRECT );@@ -1202,7 +1203,7 @@ xfs_atomic_write_cow_iomap_begin( found:if (cmap.br_state !=XFS_EXT_NORM ) { error =xfs_reflink_convert_cow_locked (ip, offset_fsb, - count_fsb); + count_fsb_orig);if (error) goto out_unlock; cmap.br_state =XFS_EXT_NORM ;@@ -1215,6 +1216,7 @@ xfs_atomic_write_cow_iomap_begin(return xfs_bmbt_to_iomap (ip, iomap, &cmap, flags,IOMAP_F_SHARED , seq);
Quoted 3 lines
I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
Hi John, this seems to be fixing the issue for me.
Thanks,
Ojaswin
Quoted 2 lines
thanks
Re: [PATCH] xfs: fix write failures in software-provided atomic writes
Quoted 4 lines · Ojaswin Mujoo
Quoted 3 lines
I think that the problem may be that we were converting an inappropriate number of blocks from unwritten to real allocations (but never writing to the excess blocks). Does it look ok?
Hi John, this seems to be fixing the issue for me.
Thanks for checking!
I'll discuss posting an updated patch with Darrick.
PATCH v7 5/12 generic: Add atomic write test using fio crc check verifier
In reply to Ojaswin MujooThis 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.
--- 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
tests/generic/1226 new file +108 -0
@@ -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_atomic_writes + +_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
tests/generic/1226.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 1226 +Silence is golden
Signature · 1 line
2.49.0
Re: [PATCH v7 05/12] generic: Add atomic write test using fio crc check verifier
Quoted 31 lines · Ojaswin Mujoo
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_ATOMICIO on a preallocated file to stress the write and end-io unwritten conversion code paths. The idea is to increase code coverage to ensureRWF_ATOMIChasn'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) <[email protected]>Signed-off-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>--- 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.outtests/generic/1226 new file +0
-0
tests/generic/1226 line 5
Quoted 5 lines
tests/generic/1226 new file +5
-0@@ -0,0 +1,108 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2025 IBM Corporation. All Rights Reserved. +# +# FS QA Test 1226
Hey Zorro,
Thanks for picking these in for-next. I just noticed that the test number for this test has become 773, but we missed changing:
# FS QA Test 1226
to
# FS QA Test 773
Regards,
Ojaswin
Quoted 114 lines
tests/generic/1226 new file +103
-0+# +# 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_atomic_writes + +_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 +exittests/generic/1226.out new file +2
-0@@ -0,0 +1,2 @@ +QA output created by 1226 +Silence is goldenSignature · 1 line
2.49.0
Re: [PATCH v7 05/12] generic: Add atomic write test using fio crc check verifier
Quoted 40 lines · Ojaswin Mujoo
Quoted 31 lines · Ojaswin Mujoo
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_ATOMICIO on a preallocated file to stress the write and end-io unwritten conversion code paths. The idea is to increase code coverage to ensureRWF_ATOMIChasn'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) <[email protected]>Signed-off-by Ritesh Harjani (IBM) <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>Reviewed-by John Garry <[email protected]>Signed-off-by Ojaswin Mujoo <[email protected]>--- 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.outtests/generic/1226 new file +0
-0tests/generic/1226 line 5
Quoted 5 lines
tests/generic/1226 new file +5
-0@@ -0,0 +1,108 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2025 IBM Corporation. All Rights Reserved. +# +# FS QA Test 1226Hey Zorro,
Quoted 8 lines
Thanks for picking these in for-next. I just noticed that the test number for this test has become 773, but we missed changing:
# FS QA Test 1226
to
# FS QA Test 773
Hmm...weird, I change the case number by running ./tools/mvtest generic/1226 generic/773. The "FS QA Test 1226" should be changed to "FS QA Test No. 773" by:
sed -e "s/^# FS[[:space:]]*QA.*Test.*[0-9]\+$/# FS QA Test No. ${did}/g" -i "tests/${dest}"I'm not sure what happened at that time, I'll check if there're more "wrong number" issues, will correct them in one patch. Thanks for reporting:)
Thanks,
Zorro
Quoted 122 lines
Regards,
OjaswinQuoted 114 lines
tests/generic/1226 new file +103
-0+# +# 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_atomic_writes + +_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 +exittests/generic/1226.out new file +2
-0@@ -0,0 +1,2 @@ +QA output created by 1226 +Silence is goldenSignature · 1 line
2.49.0
PATCH v7 6/12 generic: Add atomic write test using fio verify on file mixed mappings
In reply to Ojaswin MujooThis 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.
--- 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
tests/generic/1227 new file +132 -0
@@ -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_atomic_writes +_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
tests/generic/1227.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 1227 +Silence is golden
Signature · 1 line
2.49.0
PATCH v7 7/12 generic: Add atomic write multi-fsblock O_[D]SYNC tests
In reply to Ojaswin MujooThis adds various atomic write multi-fsblock stress tests with mixed mappings and O_SYNC, to ensure the data and metadata is atomically persisted even if there is a shutdown.
--- tests/generic/1228 | 138 +++++++++++++++++++++++++++++++++++++++++ tests/generic/1228.out | 2 + 2 files changed, 140 insertions(+) create mode 100755 tests/generic/1228 create mode 100644 tests/generic/1228.out
tests/generic/1228 new file +138 -0
@@ -0,0 +1,138 @@ +#! /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 + +iterations=10 +for ((i=1; i<=$iterations; i++)); do + echo "=== Mixed Mapping Test Iteration $i ===" >> $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 $i completed: OK" >> $seqres.full + echo >> $seqres.full +done +echo "# Test 1: Do O_SYNC atomic write on random mixed mapping ($iterations 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
tests/generic/1228.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 1228 +Silence is golden
Signature · 1 line
2.49.0
PATCH v7 8/12 generic: Stress fsx with atomic writes enabled
In reply to Ojaswin MujooStress file with atomic writes to ensure we exercise codepaths where we are mixing different FS operations with atomic writes
--- 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
tests/generic/1229 new file +68 -0
@@ -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/collapse range support for ext4+bigalloc is + # currently broken, so disable it. Also disable in case 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
tests/generic/1229.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 1229 +Silence is golden
Signature · 1 line
2.49.0
PATCH v7 9/12 generic: Add sudden shutdown tests for multi block atomic writes
In reply to Ojaswin MujooThis 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 parallelly 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
--- tests/generic/1230 | 368 +++++++++++++++++++++++++++++++++++++++++ tests/generic/1230.out | 2 + 2 files changed, 370 insertions(+) create mode 100755 tests/generic/1230 create mode 100644 tests/generic/1230.out
tests/generic/1230 new file +368 -0
@@ -0,0 +1,368 @@ +#! /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 +} + +start_atomic_write_and_shutdown() { + atomic_write_loop & + awloop_pid=$! + + local i=0 + # Wait for at least 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 +} + +# This test has the following flow: +# 1. Start doing sequential atomic writes in background, 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 + + start_atomic_write_and_shutdown + + 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 for torn write 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 "Old: " + echo "$expected_data_old" + echo + echo "New: " + echo "$expected_data_new" + echo + echo "Actual contents: " + echo "$actual_data" + + _fail + fi + echo -n "Check at offset $off succeeded! " >> $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 torn write for file by shutting down in between atomic writes +test_torn_write() { + echo >> $seqres.full + echo "# Writing atomically to file in background" >> $seqres.full + + start_atomic_write_and_shutdown + + 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 happened + 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 torn write for file with written and unwritten mappings +test_torn_write_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_torn_write + + verify_data_blocks $verify_start $verify_end "$expected_data_old_mixed" "$expected_data_new" +} + +# test torn write for file with completely written mappings +test_torn_write_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_torn_write + + verify_data_blocks $verify_start $verify_end "$expected_data_old_mapped" "$expected_data_new" +} + +# test torn write for file with completely unwritten mappings +test_torn_write_unwritten() { + $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_torn_write + + verify_data_blocks $verify_start $verify_end "$expected_data_old_zeroes" "$expected_data_new" +} + +# test torn write for file with no mappings +test_torn_write_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_torn_write + + verify_data_blocks $verify_start $verify_end "$expected_data_old_zeroes" "$expected_data_new" +} + +test_append_torn_write() { + $XFS_IO_PROG -c "truncate 0" $testfile >> $seqres.full + + echo >> $seqres.full + echo "# Performing append atomic writes over file in background" >> $seqres.full + + start_atomic_write_and_shutdown + + 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 torn write test for atomic writes over mixed mapping" >> $seqres.full + test_torn_write_mixed + + echo >> $seqres.full + echo "# Starting torn write test for atomic writes over fully written mapping" >> $seqres.full + test_torn_write_written + + echo >> $seqres.full + echo "# Starting torn write test for atomic writes over fully unwritten mapping" >> $seqres.full + test_torn_write_unwritten + + echo >> $seqres.full + echo "# Starting torn write test for atomic writes over holes" >> $seqres.full + test_torn_write_hole + + echo >> $seqres.full + echo "# Starting shutdown torn write test for append atomic writes" >> $seqres.full + test_append_torn_write +done + +echo "Silence is golden" +status=0 +exit
tests/generic/1230.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 1230 +Silence is golden
Signature · 1 line
2.49.0
PATCH v7 10/12 ext4: Test atomic write and ioend codepaths with bigalloc
In reply to Ojaswin MujooFrom: "Ritesh Harjani (IBM)" <[email protected]>
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.
--- 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
tests/ext4/061 new file +155 -0
@@ -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_fio_atomic_writes +_require_aiodio + +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 || return + 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
tests/ext4/061.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 061 +Silence is golden
Signature · 1 line
2.49.0
PATCH v7 11/12 ext4: Test atomic writes allocation and write codepaths with bigalloc
In reply to Ojaswin MujooFrom: "Ritesh Harjani (IBM)" <[email protected]>
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.
--- 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
tests/ext4/062 new file +203 -0
@@ -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_fio_atomic_writes +_require_aiodio + +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 || return + 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
tests/ext4/062.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 062 +Silence is golden
Signature · 1 line
2.49.0
PATCH v7 12/12 ext4: Atomic write test for extent split across leaf nodes
In reply to Ojaswin MujooIn 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
--- 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
tests/ext4/063 new file +129 -0
@@ -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 succeeded!" >> $seqres.full +echo "Silence is golden" +status=0 +exit
tests/ext4/063.out new file +2 -0
@@ -0,0 +1,2 @@ +QA output created by 063 +Silence is golden
Signature · 1 line
2.49.0