Linux Btrfs filesystem development
 help / color / mirror / Atom feed
From: Qu Wenruo <wqu@suse.com>
To: linux-btrfs@vger.kernel.org
Subject: [PATCH] fstests: btrfs: add a new regression test for RAID56 RWM
Date: Sun, 12 Jul 2026 19:27:08 +0930	[thread overview]
Message-ID: <20260712095708.182997-1-wqu@suse.com> (raw)

There is a regression in btrfs RAID56 RMW, that data checksum
verification can be skipped incorrectly, causing RMW to use the already
corrupted data to generate parity, killing the last chance to recover
the data.

The test case create a special raid5 full stripe layout initially:

            0                      32K                               64K
  Data 1    |    file1, no csum    |   file 2, has csum               |
  Data 2    |                  Unused                                 |
  Parity    |                Parity                                   |

Then corrupt the on-disk data of file 2:

            0                      32K                               64K
  Data 1    |    file1, no csum    |   file 2, corrupted, has csum    |
  Data 2    |                  Unused                                 |
  Parity    |                Parity                                   |

At this stage, if we read out file2, we can still rebuild the data using
Data 2 + Parity.

Instead do a new write to populate the unused data stripe:

            0                      32K                               64K
  Data 1    |    file1, no csum    |   file 2, corrupted, has csum    |
  Data 2    |    file3, new write                                     |
  Parity    |                Parity                                   |

During RMW we should only skip data checksum for range [0, 32K) of data
stripe 1, and still do checksum verification for range [32K, 64K) of
data stripe 1.

But a regression cause the kernel to skip data checksum verification for
the whole range of [0, 64K).
So RMW will use the corrupted data of file 2 to generate parity, and
cause file2 to be permanently corrupted.

For patched kernel or before the regression, RMW will verify the
checksum of range [32K, 64K) of data 1, and rebuild using parity and
data 2 before the new write.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 tests/btrfs/352     | 103 ++++++++++++++++++++++++++++++++++++++++++++
 tests/btrfs/352.out |   4 ++
 2 files changed, 107 insertions(+)
 create mode 100755 tests/btrfs/352
 create mode 100644 tests/btrfs/352.out

diff --git a/tests/btrfs/352 b/tests/btrfs/352
new file mode 100755
index 00000000..435a3ec7
--- /dev/null
+++ b/tests/btrfs/352
@@ -0,0 +1,103 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 SUSE S.A.  All Rights Reserved.
+#
+# FS QA Test 352
+#
+# Make sure RAID5 RMW detects corruption correctly.
+#
+. ./common/preamble
+_begin_fstest auto quick volume raid scrub
+
+# For _btrfs_get_first_logical()
+. ./common/filter
+
+# No compression/nodatasum/nodatacow, as we require every
+# data write to be as is on-disk, and we need to control
+# datacsum on a per-file basis, so no global nodatacow/nodatasum
+# mount options allowed
+_require_btrfs_no_compress
+_require_btrfs_no_nodatacow
+_require_btrfs_no_nodatasum
+_require_chattr C
+
+if ! _check_btrfs_raid_type raid5; then
+	_notrun "requires either raid5 or raid6 support"
+fi
+
+# The file layout are all based on 4K block size.
+_require_btrfs_support_sectorsize 4096
+_require_scratch_dev_pool 3
+
+_fixed_by_kernel_commit xxxxxxxxxxxx \
+	"btrfs: raid56: fix an incorrect csum skip during rmw"
+
+_require_non_zoned_device "${SCRATCH_DEV}"
+_require_scratch_dev_pool 3
+_scratch_dev_pool_get 3
+_scratch_pool_mkfs -d raid5 -m raid1  >> $seqres.full 2>&1
+
+# Use v2 cache, as v1 cache will use data space and screw up
+# the data layout inside a full stripe.
+_scratch_mount -o space_cache=v2,datasum
+
+# Balance data so that new data writes can always start at the beginning
+# of the data chunk.
+# The mkfs may put the first data stripe at a location that covers the
+# 2nd super block, that will cause the first data to be at the middle of the chunk
+# making it very hard to force the file layout.
+$BTRFS_UTIL_PROG balance start -d $SCRATCH_MNT >> $seqres.full
+
+chunk_logical=$($BTRFS_UTIL_PROG inspect-internal dump-tree -t chunk $SCRATCH_DEV |\
+	grep -B1 RAID5 | grep -o "[[:digit:]]\+)" | sed 's/)//')
+if [ -z "$chunk_logical" ]; then
+	_scratch_unmount
+	_scratch_dev_pool_put
+	_fail "failed to grab the chunk logical bytenr"
+fi
+
+touch $SCRATCH_MNT/file1
+chattr +C $SCRATCH_MNT/file1
+$XFS_IO_PROG -c "pwrite -S 0xcd 0 32K" -c sync \
+	$SCRATCH_MNT/file1 >> $seqres.full
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/file1)
+
+if [ "$logical" -ne "$chunk_logical" ]; then
+	_scratch_unmount
+	_scratch_dev_pool_put
+	_notrun "unable to ensure the required file layout"
+fi
+
+# The next 32K data will have data checksum
+$XFS_IO_PROG -f -c "pwrite -S 0xdc 0 32K" -c sync \
+	$SCRATCH_MNT/file2 >> $seqres.full
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/file2)
+
+# Corrupt the data stripe which has checksum
+physical_d=$(_btrfs_get_physical ${logical} 1)
+devpath_d=$(_btrfs_get_device_path ${logical} 1)
+
+_scratch_unmount
+
+echo "Corrupt data stripe at devpath $devpath_d physical $physical_d" \
+	>> $seqres.full
+$XFS_IO_PROG -c "pwrite -S 0x00 $physical_d 64K" -c sync $devpath_d \
+	>> $seqres.full
+
+$BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+_scratch_mount -o space_cache=v2,datasum
+
+# Do a write that updates part of the stripe. This requires RMW, and
+# with unpatched kernel, the csum verification will be skipped and generate
+# parity using the corrupted data.
+$XFS_IO_PROG -f -c "pwrite -S 0x42 0 64K" $SCRATCH_MNT/file3 -c sync \
+	>> $seqres.full
+
+# Now check if we can still read out the data at stripe offset 32K
+_hexdump $SCRATCH_MNT/file2
+
+_scratch_unmount
+_scratch_dev_pool_put
+# success, all done
+_exit 0
diff --git a/tests/btrfs/352.out b/tests/btrfs/352.out
new file mode 100644
index 00000000..972993b6
--- /dev/null
+++ b/tests/btrfs/352.out
@@ -0,0 +1,4 @@
+QA output created by 352
+000000 dc dc dc dc dc dc dc dc dc dc dc dc dc dc dc dc  >................<
+*
+008000
-- 
2.51.2


             reply	other threads:[~2026-07-12  9:57 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-12  9:57 Qu Wenruo [this message]
  -- strict thread matches above, loose matches on Subject: below --
2026-07-12  9:57 [PATCH] fstests: btrfs: add a new regression test for RAID56 RWM Qu Wenruo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260712095708.182997-1-wqu@suse.com \
    --to=wqu@suse.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox