[PATCH v5 11/12] ext4: Test atomic writes allocation and write codepaths with bigalloc

Ojaswin Mujoo posted 11 patches 2 weeks, 1 day ago
[PATCH v5 11/12] ext4: Test atomic writes allocation and write codepaths with bigalloc
Posted by Ojaswin Mujoo 2 weeks, 1 day ago
From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>

This test does a parallel RWF_ATOMIC IO on a multiple truncated files in
a small FS. The idea is to stress ext4 allocator to ensure we are able
to handle low space scenarios correctly with atomic writes. We brute
force this for different blocksize and clustersizes and after each
iteration we ensure the data was not torn or corrupted using fio crc
verification.

Note that in this test we use overlapping atomic writes of same io size.
Although serializing racing writes is not guaranteed for RWF_ATOMIC,
NVMe and SCSI provide this guarantee as an inseparable feature to
power-fail atomicity. Keeping the iosize as same also ensures that ext4
doesn't tear the write due to racing ioend unwritten conversion.

The value of this test is that we make sure the RWF_ATOMIC is handled
correctly by ext4 as well as test that the block layer doesn't split or
only generate multiple bios for an atomic write.

Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
---
 tests/ext4/062     | 203 +++++++++++++++++++++++++++++++++++++++++++++
 tests/ext4/062.out |   2 +
 2 files changed, 205 insertions(+)
 create mode 100755 tests/ext4/062
 create mode 100644 tests/ext4/062.out

diff --git a/tests/ext4/062 b/tests/ext4/062
new file mode 100755
index 00000000..d48f69d3
--- /dev/null
+++ b/tests/ext4/062
@@ -0,0 +1,203 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test 062
+#
+# This test does a parallel RWF_ATOMIC IO on a multiple truncated files in a
+# small FS. The idea is to stress ext4 allocator to ensure we are able to
+# handle low space scenarios correctly with atomic writes.. We brute force this
+# for all possible blocksize and clustersizes and after each iteration we
+# ensure the data was not torn or corrupted using fio crc verification.
+#
+# Note that in this test we use overlapping atomic writes of same io size.
+# Although serializing racing writes is not guaranteed for RWF_ATOMIC, NVMe and
+# SCSI provide this guarantee as an inseparable feature to power-fail
+# atomicity. Keeping the iosize as same also ensures that ext4 doesn't tear the
+# write due to racing ioend unwritten conversion.
+#
+# The value of this test is that we make sure the RWF_ATOMIC is handled
+# correctly by ext4 as well as test that the block layer doesn't split or only
+# generate multiple bios for an atomic write.
+#
+
+. ./common/preamble
+. ./common/atomicwrites
+
+_begin_fstest auto rw stress atomicwrites
+
+_require_scratch_write_atomic
+_require_aiodio
+_require_fio_version "3.38+"
+
+FSSIZE=$((360*1024*1024))
+FIO_LOAD=$(($(nproc) * LOAD_FACTOR))
+
+# Calculate bs as per bdev atomic write units.
+bdev_awu_min=$(_get_atomic_write_unit_min $SCRATCH_DEV)
+bdev_awu_max=$(_get_atomic_write_unit_max $SCRATCH_DEV)
+bs=$(_max 4096 "$bdev_awu_min")
+
+function create_fio_configs()
+{
+	local bsize=$1
+	create_fio_aw_config $bsize
+	create_fio_verify_config $bsize
+}
+
+function create_fio_verify_config()
+{
+	local bsize=$1
+cat >$fio_verify_config <<EOF
+	[global]
+	direct=1
+	ioengine=libaio
+	rw=read
+	bs=$bsize
+	fallocate=truncate
+	size=$((FSSIZE / 12))
+	iodepth=$FIO_LOAD
+	numjobs=$FIO_LOAD
+	group_reporting=1
+	atomic=1
+
+	verify_only=1
+	verify_state_save=0
+	verify=crc32c
+	verify_fatal=1
+	verify_write_sequence=0
+
+	[verify-job1]
+	filename=$SCRATCH_MNT/testfile-job1
+
+	[verify-job2]
+	filename=$SCRATCH_MNT/testfile-job2
+
+	[verify-job3]
+	filename=$SCRATCH_MNT/testfile-job3
+
+	[verify-job4]
+	filename=$SCRATCH_MNT/testfile-job4
+
+	[verify-job5]
+	filename=$SCRATCH_MNT/testfile-job5
+
+	[verify-job6]
+	filename=$SCRATCH_MNT/testfile-job6
+
+	[verify-job7]
+	filename=$SCRATCH_MNT/testfile-job7
+
+	[verify-job8]
+	filename=$SCRATCH_MNT/testfile-job8
+
+EOF
+}
+
+function create_fio_aw_config()
+{
+	local bsize=$1
+cat >$fio_aw_config <<EOF
+	[global]
+	direct=1
+	ioengine=libaio
+	rw=randwrite
+	bs=$bsize
+	fallocate=truncate
+	size=$((FSSIZE / 12))
+	iodepth=$FIO_LOAD
+	numjobs=$FIO_LOAD
+	group_reporting=1
+	atomic=1
+
+	verify_state_save=0
+	verify=crc32c
+	do_verify=0
+
+	[write-job1]
+	filename=$SCRATCH_MNT/testfile-job1
+
+	[write-job2]
+	filename=$SCRATCH_MNT/testfile-job2
+
+	[write-job3]
+	filename=$SCRATCH_MNT/testfile-job3
+
+	[write-job4]
+	filename=$SCRATCH_MNT/testfile-job4
+
+	[write-job5]
+	filename=$SCRATCH_MNT/testfile-job5
+
+	[write-job6]
+	filename=$SCRATCH_MNT/testfile-job6
+
+	[write-job7]
+	filename=$SCRATCH_MNT/testfile-job7
+
+	[write-job8]
+	filename=$SCRATCH_MNT/testfile-job8
+
+EOF
+}
+
+run_test_one() {
+	local bs=$1
+	local cs=$2
+	local iosize=$3
+
+	MKFS_OPTIONS="-O bigalloc -b $bs -C $cs"
+	_scratch_mkfs_sized "$FSSIZE" >> $seqres.full 2>&1 || continue
+	if _try_scratch_mount >> $seqres.full 2>&1; then
+		echo "Testing: bs=$bs cs=$cs iosize=$iosize" >> $seqres.full
+
+		touch $SCRATCH_MNT/f1
+		create_fio_configs $iosize
+
+		cat $fio_aw_config >> $seqres.full
+		cat $fio_verify_config >> $seqres.full
+
+		$FIO_PROG $fio_aw_config >> $seqres.full
+		ret1=$?
+
+		$FIO_PROG $fio_verify_config  >> $seqres.full
+		ret2=$?
+
+		_scratch_unmount
+
+		[[ $ret1 -eq 0 && $ret2 -eq 0 ]] || _fail "fio with atomic write failed"
+	fi
+}
+
+run_test() {
+	local bs=$1
+
+	# cluster sizes above 16 x blocksize are experimental so avoid them
+	# Also, cap cluster size at 128kb to keep it reasonable for large
+	# blocks size
+	max_cs=$(_min $((16 * bs)) "$bdev_awu_max" $((128 * 1024)))
+
+	# Fuzz for combinations of blocksize, clustersize and
+	# iosize that cover most of the cases
+	run_test_one $bs $bs $bs
+	run_test_one $bs $max_cs $bs
+	run_test_one $bs $max_cs $max_cs
+	run_test_one $bs $max_cs $(_max "$((max_cs/2))" $bs)
+}
+
+# Let's create a sample fio config to check whether fio supports all options.
+fio_aw_config=$tmp.aw.fio
+fio_verify_config=$tmp.verify.fio
+fio_out=$tmp.fio.out
+
+create_fio_configs $bs
+_require_fio $fio_aw_config
+
+for ((bs=$bs; bs <= $(_get_page_size); bs = $bs << 1)); do
+	run_test $bs $cs $iosize
+done
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/ext4/062.out b/tests/ext4/062.out
new file mode 100644
index 00000000..a1578f48
--- /dev/null
+++ b/tests/ext4/062.out
@@ -0,0 +1,2 @@
+QA output created by 062
+Silence is golden
-- 
2.49.0
Re: [PATCH v5 11/12] ext4: Test atomic writes allocation and write codepaths with bigalloc
Posted by John Garry 4 days, 11 hours ago
On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
> 
> This test does a parallel RWF_ATOMIC IO on a multiple truncated files in
> a small FS. The idea is to stress ext4 allocator to ensure we are able
> to handle low space scenarios correctly with atomic writes. We brute
> force this for different blocksize and clustersizes and after each
> iteration we ensure the data was not torn or corrupted using fio crc
> verification.
> 
> Note that in this test we use overlapping atomic writes of same io size.
> Although serializing racing writes is not guaranteed for RWF_ATOMIC,
> NVMe and SCSI provide this guarantee as an inseparable feature to
> power-fail atomicity. Keeping the iosize as same also ensures that ext4
> doesn't tear the write due to racing ioend unwritten conversion.
> 
> The value of this test is that we make sure the RWF_ATOMIC is handled
> correctly by ext4 as well as test that the block layer doesn't split or
> only generate multiple bios for an atomic write.
> 
> Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> ---
>   tests/ext4/062     | 203 +++++++++++++++++++++++++++++++++++++++++++++
>   tests/ext4/062.out |   2 +
>   2 files changed, 205 insertions(+)
>   create mode 100755 tests/ext4/062
>   create mode 100644 tests/ext4/062.out
> 
> diff --git a/tests/ext4/062 b/tests/ext4/062
> new file mode 100755
> index 00000000..d48f69d3
> --- /dev/null
> +++ b/tests/ext4/062
> @@ -0,0 +1,203 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
> +#
> +# FS QA Test 062
> +#
> +# This test does a parallel RWF_ATOMIC IO on a multiple truncated files in a
> +# small FS. The idea is to stress ext4 allocator to ensure we are able to
> +# handle low space scenarios correctly with atomic writes.. We brute force this
> +# for all possible blocksize and clustersizes and after each iteration we
> +# ensure the data was not torn or corrupted using fio crc verification.
> +#
> +# Note that in this test we use overlapping atomic writes of same io size.
> +# Although serializing racing writes is not guaranteed for RWF_ATOMIC, NVMe and
> +# SCSI provide this guarantee as an inseparable feature to power-fail
> +# atomicity. Keeping the iosize as same also ensures that ext4 doesn't tear the
> +# write due to racing ioend unwritten conversion.
> +#
> +# The value of this test is that we make sure the RWF_ATOMIC is handled
> +# correctly by ext4 as well as test that the block layer doesn't split or only
> +# generate multiple bios for an atomic write.
> +#
> +
> +. ./common/preamble
> +. ./common/atomicwrites
> +
> +_begin_fstest auto rw stress atomicwrites
> +
> +_require_scratch_write_atomic
> +_require_aiodio
> +_require_fio_version "3.38+"
> +
> +FSSIZE=$((360*1024*1024))
> +FIO_LOAD=$(($(nproc) * LOAD_FACTOR))
> +
> +# Calculate bs as per bdev atomic write units.
> +bdev_awu_min=$(_get_atomic_write_unit_min $SCRATCH_DEV)
> +bdev_awu_max=$(_get_atomic_write_unit_max $SCRATCH_DEV)
> +bs=$(_max 4096 "$bdev_awu_min")
> +
> +function create_fio_configs()
> +{
> +	local bsize=$1
> +	create_fio_aw_config $bsize
> +	create_fio_verify_config $bsize
> +}
> +
> +function create_fio_verify_config()
> +{
> +	local bsize=$1
> +cat >$fio_verify_config <<EOF
> +	[global]
> +	direct=1
> +	ioengine=libaio
> +	rw=read
> +	bs=$bsize
> +	fallocate=truncate
> +	size=$((FSSIZE / 12))
> +	iodepth=$FIO_LOAD
> +	numjobs=$FIO_LOAD
> +	group_reporting=1
> +	atomic=1
> +
> +	verify_only=1
> +	verify_state_save=0
> +	verify=crc32c
> +	verify_fatal=1
> +	verify_write_sequence=0
> +
> +	[verify-job1]
> +	filename=$SCRATCH_MNT/testfile-job1
> +
> +	[verify-job2]
> +	filename=$SCRATCH_MNT/testfile-job2
> +
> +	[verify-job3]
> +	filename=$SCRATCH_MNT/testfile-job3
> +
> +	[verify-job4]
> +	filename=$SCRATCH_MNT/testfile-job4
> +
> +	[verify-job5]
> +	filename=$SCRATCH_MNT/testfile-job5
> +
> +	[verify-job6]
> +	filename=$SCRATCH_MNT/testfile-job6
> +
> +	[verify-job7]
> +	filename=$SCRATCH_MNT/testfile-job7
> +
> +	[verify-job8]
> +	filename=$SCRATCH_MNT/testfile-job8

do you really need multiple jobs for verify?
Re: [PATCH v5 11/12] ext4: Test atomic writes allocation and write codepaths with bigalloc
Posted by Ojaswin Mujoo 1 day, 9 hours ago
On Tue, Sep 02, 2025 at 04:54:48PM +0100, John Garry wrote:
> On 22/08/2025 09:02, Ojaswin Mujoo wrote:
> > From: "Ritesh Harjani (IBM)" <ritesh.list@gmail.com>
> > 
> > This test does a parallel RWF_ATOMIC IO on a multiple truncated files in
> > a small FS. The idea is to stress ext4 allocator to ensure we are able
> > to handle low space scenarios correctly with atomic writes. We brute
> > force this for different blocksize and clustersizes and after each
> > iteration we ensure the data was not torn or corrupted using fio crc
> > verification.
> > 
> > Note that in this test we use overlapping atomic writes of same io size.
> > Although serializing racing writes is not guaranteed for RWF_ATOMIC,
> > NVMe and SCSI provide this guarantee as an inseparable feature to
> > power-fail atomicity. Keeping the iosize as same also ensures that ext4
> > doesn't tear the write due to racing ioend unwritten conversion.
> > 
> > The value of this test is that we make sure the RWF_ATOMIC is handled
> > correctly by ext4 as well as test that the block layer doesn't split or
> > only generate multiple bios for an atomic write.
> > 
> > Signed-off-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
> > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> > ---
> >   tests/ext4/062     | 203 +++++++++++++++++++++++++++++++++++++++++++++
> >   tests/ext4/062.out |   2 +
> >   2 files changed, 205 insertions(+)
> >   create mode 100755 tests/ext4/062
> >   create mode 100644 tests/ext4/062.out
> > 
> > diff --git a/tests/ext4/062 b/tests/ext4/062
> > new file mode 100755
> > index 00000000..d48f69d3
> > --- /dev/null
> > +++ b/tests/ext4/062
> > @@ -0,0 +1,203 @@
> > +#! /bin/bash
> > +# SPDX-License-Identifier: GPL-2.0
> > +# Copyright (c) 2025 IBM Corporation. All Rights Reserved.
> > +#
> > +# FS QA Test 062
> > +#
> > +# This test does a parallel RWF_ATOMIC IO on a multiple truncated files in a
> > +# small FS. The idea is to stress ext4 allocator to ensure we are able to
> > +# handle low space scenarios correctly with atomic writes.. We brute force this
> > +# for all possible blocksize and clustersizes and after each iteration we
> > +# ensure the data was not torn or corrupted using fio crc verification.
> > +#
> > +# Note that in this test we use overlapping atomic writes of same io size.
> > +# Although serializing racing writes is not guaranteed for RWF_ATOMIC, NVMe and
> > +# SCSI provide this guarantee as an inseparable feature to power-fail
> > +# atomicity. Keeping the iosize as same also ensures that ext4 doesn't tear the
> > +# write due to racing ioend unwritten conversion.
> > +#
> > +# The value of this test is that we make sure the RWF_ATOMIC is handled
> > +# correctly by ext4 as well as test that the block layer doesn't split or only
> > +# generate multiple bios for an atomic write.
> > +#
> > +
> > +. ./common/preamble
> > +. ./common/atomicwrites
> > +
> > +_begin_fstest auto rw stress atomicwrites
> > +
> > +_require_scratch_write_atomic
> > +_require_aiodio
> > +_require_fio_version "3.38+"
> > +
> > +FSSIZE=$((360*1024*1024))
> > +FIO_LOAD=$(($(nproc) * LOAD_FACTOR))
> > +
> > +# Calculate bs as per bdev atomic write units.
> > +bdev_awu_min=$(_get_atomic_write_unit_min $SCRATCH_DEV)
> > +bdev_awu_max=$(_get_atomic_write_unit_max $SCRATCH_DEV)
> > +bs=$(_max 4096 "$bdev_awu_min")
> > +
> > +function create_fio_configs()
> > +{
> > +	local bsize=$1
> > +	create_fio_aw_config $bsize
> > +	create_fio_verify_config $bsize
> > +}
> > +
> > +function create_fio_verify_config()
> > +{
> > +	local bsize=$1
> > +cat >$fio_verify_config <<EOF
> > +	[global]
> > +	direct=1
> > +	ioengine=libaio
> > +	rw=read
> > +	bs=$bsize
> > +	fallocate=truncate
> > +	size=$((FSSIZE / 12))
> > +	iodepth=$FIO_LOAD
> > +	numjobs=$FIO_LOAD
> > +	group_reporting=1
> > +	atomic=1
> > +
> > +	verify_only=1
> > +	verify_state_save=0
> > +	verify=crc32c
> > +	verify_fatal=1
> > +	verify_write_sequence=0
> > +
> > +	[verify-job1]
> > +	filename=$SCRATCH_MNT/testfile-job1
> > +
> > +	[verify-job2]
> > +	filename=$SCRATCH_MNT/testfile-job2
> > +
> > +	[verify-job3]
> > +	filename=$SCRATCH_MNT/testfile-job3
> > +
> > +	[verify-job4]
> > +	filename=$SCRATCH_MNT/testfile-job4
> > +
> > +	[verify-job5]
> > +	filename=$SCRATCH_MNT/testfile-job5
> > +
> > +	[verify-job6]
> > +	filename=$SCRATCH_MNT/testfile-job6
> > +
> > +	[verify-job7]
> > +	filename=$SCRATCH_MNT/testfile-job7
> > +
> > +	[verify-job8]
> > +	filename=$SCRATCH_MNT/testfile-job8
> 
> do you really need multiple jobs for verify?

Yes since we want each job to verify it's own file.
> 
>