* [PATCH] ext4: test reads after a failed zeroout of an unwritten extent
@ 2026-10-08 7:39 ` Daejun Park
0 siblings, 0 replies; 5+ 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] 5+ messages in thread
* [PATCH] ext4: test reads after a failed zeroout of an unwritten extent
@ 2026-10-08 7:39 ` Daejun Park
0 siblings, 0 replies; 5+ messages in thread
From: Daejun Park @ 2026-10-08 7:39 UTC (permalink / raw)
To: fstests
Cc: linux-ext4, Zorro Lang, Ojaswin Mujoo, Jan Kara,
Theodore Ts'o, Daejun Park
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] 5+ messages in thread
* Re: [PATCH] ext4: test reads after a failed zeroout of an unwritten extent
2026-10-08 7:39 ` Daejun Park
(?)
@ 2026-10-08 7:47 ` sashiko-bot
-1 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-08 7:47 UTC (permalink / raw)
To: Daejun Park; +Cc: tytso, linux-ext4
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] fs/ext4: Script submitted to wrong repository (fstests vs kernel)
- [Medium] ext4/xfstests: Typo in _require_scratch_ext4_feature_enabled function call
--
commit 15df6b5040afb28bc9e8fcc362919219d21014ba
Author: Daejun Park <daejun7.park@samsung.com>
ext4: test reads after a failed zeroout of an unwritten extent
This commit adds a new test script to verify that reading blocks from an
unwritten extent returns zeroes after a failed zeroout operation. It uses
dm-error to simulate the failure and ensures ext4 doesn't cache the unzeroed
blocks.
Link: https://lore.kernel.org/r/20261008-ext4-fc-zeroout-v2-1-55cab1e24fa3@samsung.com
> diff --git a/tests/ext4/309 b/tests/ext4/309
> new file mode 100755
> index 0000000000000..1fafd58263577
> --- /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
[ ... ]
> +. ./common/preamble
> +_begin_fstest auto quick rw prealloc eio
[Severity: Medium]
Does this script belong in the fstests repository instead of the Linux kernel
tree?
The use of the common preamble and _begin_fstest demonstrates a dependency
on the xfstests framework. Applying this patch to the kernel tree will
result in a non-functional script with unresolved dependencies.
[ ... ]
> +_require_scratch
> +_require_dm_target error
> +_require_xfs_io_command "falloc"
> +_require_xfs_io_command "fiemap"
> +_require_scratch_ext4_feature_enabled "extent"
[Severity: Medium]
Is there a typo in this function call?
The function _require_scratch_ext4_feature_enabled does not appear to exist
and should likely be _require_scratch_ext4_feature. This requirement check
will fail silently (printing an error but continuing execution), potentially
causing the test to run on unsupported configurations.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261008-ext4-zeroout-eio-test-v1-1-9bb66deaf646@samsung.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] ext4: test reads after a failed zeroout of an unwritten extent
2026-10-08 7:39 ` Daejun Park
(?)
(?)
@ 2026-10-08 9:38 ` Christoph Hellwig
2026-10-08 9:44 ` Daejun Park
-1 siblings, 1 reply; 5+ 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] 5+ 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; 5+ 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] 5+ messages in thread
end of thread, other threads:[~2026-10-08 9:44 UTC | newest]
Thread overview: 5+ 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 7:39 ` Daejun Park
2026-10-08 7:47 ` sashiko-bot
2026-10-08 9:38 ` Christoph Hellwig
2026-10-08 9:44 ` Daejun Park
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.