FS/XFS testing framework
 help / color / mirror / Atom feed
* [PATCH] ext4: test reads after a failed zeroout of an unwritten extent
@ 2026-10-08  7:39 Daejun Park via B4 Relay
  2026-10-08  9:38 ` Christoph Hellwig
  0 siblings, 1 reply; 3+ messages in thread
From: Daejun Park via B4 Relay @ 2026-10-08  7:39 UTC (permalink / raw)
  To: fstests
  Cc: linux-ext4, Zorro Lang, Ojaswin Mujoo, Jan Kara,
	Theodore Ts'o, Daejun Park

From: Daejun Park <daejun7.park@samsung.com>

When a write converts part of a small unwritten extent, ext4 may zero
out the rest of the extent and convert all of it to written. If that
zeroout fails, ext4 falls back to splitting the extent, but it cached
the blocks it failed to zero out as written in the extent status tree,
so reads of those blocks returned the old contents of the disk.

Make the zeroout fail with dm-error and check that those blocks still
read back as zeroes. The test mounts with dioread_lock and nodelalloc:
with the default dioread_nolock, the extent stays unwritten until the
I/O completes and is converted then, without this zeroout. generic/250
and generic/252 do not hit it either, as they write with direct I/O
while dm-error is loaded, which also leaves the extent unwritten.

The kernel fix is "ext4: don't cache unzeroed blocks as written after a
failed zeroout".

Link: https://lore.kernel.org/r/20261008-ext4-fc-zeroout-v2-1-55cab1e24fa3@samsung.com
Suggested-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
The kernel fix is 1/2 of the series in the Link: and is not merged yet,
hence xxxxxxxxxxxx in _fixed_by_kernel_commit. The test number may need
to change.

Tested in QEMU on ext4 dev 9091c97be340. Without the fix, blocks 1-7
read back as 0x53 with 1k, 4k and 64k blocks. With the fix, alone or
with the rest of the series, the test passes, also with -O fast_commit.
---
 tests/ext4/309     | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/ext4/309.out |  5 ++++
 2 files changed, 92 insertions(+)

diff --git a/tests/ext4/309 b/tests/ext4/309
new file mode 100755
index 0000000..1fafd58
--- /dev/null
+++ b/tests/ext4/309
@@ -0,0 +1,87 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Samsung Electronics Co., Ltd. All Rights Reserved.
+#
+# FS QA Test No. 309
+#
+# When a write converts part of a small unwritten extent, ext4 may zero out
+# the rest of the extent and convert all of it to written. If that zeroout
+# fails, it falls back to splitting the extent, but it used to cache the
+# blocks it failed to zero out as written in the extent status tree, so
+# reading them returned the old contents of the disk. Fail the zeroout with
+# dm-error and check that those blocks still read back as zeroes.
+#
+. ./common/preamble
+_begin_fstest auto quick rw prealloc eio
+
+_cleanup()
+{
+	cd /
+	rm -f $tmp.*
+	_dmerror_cleanup
+}
+
+. ./common/dmerror
+
+_require_scratch
+_require_dm_target error
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "fiemap"
+_require_scratch_ext4_feature_enabled "extent"
+
+_fixed_by_kernel_commit xxxxxxxxxxxx \
+	"ext4: don't cache unzeroed blocks as written after a failed zeroout"
+
+# The zeroout is done when a write converts the extent itself. With the
+# default dioread_nolock, the extent stays unwritten until the I/O completes,
+# so mount with dioread_lock, and with nodelalloc to convert the extent at
+# write time.
+mount_dioread_lock()
+{
+	_dmerror_mount -o nodelalloc,dioread_lock >> $seqres.full 2>&1 || \
+		_fail "mount failed"
+}
+
+_scratch_mkfs >> $seqres.full 2>&1
+_dmerror_init
+mount_dioread_lock
+
+nblks=8
+blksz=$(_get_file_block_size $SCRATCH_MNT)
+sects_per_blk=$((blksz / 512))
+file=$SCRATCH_MNT/file
+
+$XFS_IO_PROG -f -c "falloc 0 $((nblks * blksz))" -c fsync $file >> $seqres.full
+extent="$($XFS_IO_PROG -c "fiemap -v" $file | grep "^[[:space:]]*0:")"
+echo "$extent" >> $seqres.full
+phys=$(echo "$extent" | $AWK_PROG '{print $3}' | sed -e 's/\.\..*//')
+len=$(echo "$extent" | $AWK_PROG '{print $4}')
+flags=$(echo "$extent" | $AWK_PROG '{print $5}')
+flags=${flags:-0}
+test "$len" = $((nblks * sects_per_blk)) -a $((flags & 0x800)) -ne 0 || \
+	_notrun "could not allocate one unwritten extent of $nblks blocks"
+
+# Put something other than zeroes in the unwritten blocks
+_dmerror_unmount
+$XFS_IO_PROG -d \
+	-c "pwrite -S 0x53 -b $blksz $((phys * 512)) $((nblks * blksz))" \
+	$DMERROR_DEV >> $seqres.full
+mount_dioread_lock
+
+# Writing block 0 zeroes out the rest of the extent if the whole extent fits
+# in extent_max_zeroout_kb, which mount resets; make that zeroout fail.
+_set_fs_sysfs_attr $DMERROR_DEV extent_max_zeroout_kb $((nblks * blksz / 1024))
+_dmerror_reset_table
+_dmerror_mark_range_bad $((phys + sects_per_blk)) \
+	$(((nblks - 1) * sects_per_blk))
+_dmerror_load_error_table
+$XFS_IO_PROG -c "pwrite -S 0x41 0 $blksz" -c fsync $file >> $seqres.full
+_dmerror_load_working_table
+
+echo "block 0:"
+od -An -v -tx1 -N $blksz $file | sort -u
+echo "blocks 1-$((nblks - 1)):"
+od -An -v -tx1 -j $blksz $file | sort -u
+
+# success, all done
+_exit 0
diff --git a/tests/ext4/309.out b/tests/ext4/309.out
new file mode 100644
index 0000000..19ed3d6
--- /dev/null
+++ b/tests/ext4/309.out
@@ -0,0 +1,5 @@
+QA output created by 309
+block 0:
+ 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
+blocks 1-7:
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

---
base-commit: 22348afe338c0f6d540c0d7ef0db749eaa51b218
change-id: 20261008-ext4-zeroout-eio-test-ee76bea0a517

Best regards,
-- 
Daejun Park <daejun7.park@samsung.com>



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] ext4: test reads after a failed zeroout of an unwritten extent
  2026-10-08  7:39 [PATCH] ext4: test reads after a failed zeroout of an unwritten extent Daejun Park via B4 Relay
@ 2026-10-08  9:38 ` Christoph Hellwig
  2026-10-08  9:44   ` Daejun Park
  0 siblings, 1 reply; 3+ messages in thread
From: Christoph Hellwig @ 2026-10-08  9:38 UTC (permalink / raw)
  To: daejun7.park
  Cc: fstests, linux-ext4, Zorro Lang, Ojaswin Mujoo, Jan Kara,
	Theodore Ts'o

On Thu, Oct 08, 2026 at 04:39:33PM +0900, Daejun Park via B4 Relay wrote:
> 
> Make the zeroout fail with dm-error and check that those blocks still
> read back as zeroes. The test mounts with dioread_lock and nodelalloc:
> with the default dioread_nolock, the extent stays unwritten until the
> I/O completes and is converted then, without this zeroout. generic/250
> and generic/252 do not hit it either, as they write with direct I/O
> while dm-error is loaded, which also leaves the extent unwritten.

Nothing in this looks ext4-specific, any reason why this is not a
generic test?


^ permalink raw reply	[flat|nested] 3+ messages in thread

* RE:(2) [PATCH] ext4: test reads after a failed zeroout of an unwritten extent
  2026-10-08  9:38 ` Christoph Hellwig
@ 2026-10-08  9:44   ` Daejun Park
  0 siblings, 0 replies; 3+ messages in thread
From: Daejun Park @ 2026-10-08  9:44 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: fstests@vger.kernel.org, linux-ext4@vger.kernel.org, Zorro Lang,
	Ojaswin Mujoo, Jan Kara, Theodore Ts'o, Daejun Park

On Thu, Oct 08, 2026 at 02:38:15AM -0700, Christoph Hellwig wrote:
> Nothing in this looks ext4-specific, any reason why this is not a
> generic test?

No good reason. I found the bug while testing ext4 and wrote this to
reproduce it, so it ended up under ext4/. You're right that it should be
generic: the nodelalloc,dioread_lock mount options and the
extent_max_zeroout_kb knob are only there to get ext4 into that path.
I'll make it a generic test in v2 and set those only for ext4.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-08  9:44 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08  7:39 [PATCH] ext4: test reads after a failed zeroout of an unwritten extent Daejun Park via B4 Relay
2026-10-08  9:38 ` Christoph Hellwig
2026-10-08  9:44   ` Daejun Park

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox