* [PATCH 7.3.y 2/5] btrfs: enhance btrfs_data_csum_ok() to use bio for page iteration
2026-11-13 14:04 ` [PATCH 7.3.y 1/5] btrfs: replace btrfs_repair_io_failure() to use bio for page iteration Sasha Levin
@ 2026-11-13 14:04 ` Sasha Levin
2026-11-13 14:04 ` [PATCH 7.3.y 3/5] btrfs: use a shared helper to calculate data checksum for a bio Sasha Levin
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-13 14:04 UTC (permalink / raw)
To: stable; +Cc: Qu Wenruo, Boris Burkov, David Sterba, Sasha Levin
From: Qu Wenruo <wqu@suse.com>
[ Upstream commit 3e4a083bf0c8bb727517ae781052b80167443077 ]
Currently btrfs_data_csum_ok() requires a @paddr[] array to iterate all
possible pages for bs > ps cases.
However for all btrfs_data_csum_ok() call sites, we already have a
btrfs_bio, and the bio infrastructure has many flexible ways to iterate
multiple pages already.
Change btrfs_data_csum_ok() to make full use of btrfs_bio by:
- Change the parameter list to require a @bvec_iter pointer
And remove @bio_offset, which can be calculated through @bvec_iter and
bbio->saved_iter.
Also remove paddrs[], we will iterate all the pages using bio
interfaces.
- Make the same parameter changes to repair_one_sector()
- Use bio interfaces to iterate pages from a bio
- Rename the function to btrfs_bio_data_csum_ok()
- Remove on-stack paddrs[] array usage
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Stable-dep-of: d00b85308abe ("btrfs: raid56: do not verify the content if there is no data checksum")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/bio.c | 82 +++++++++++++++---------------------------
fs/btrfs/btrfs_inode.h | 4 +--
fs/btrfs/inode.c | 49 +++++++++++++++++++------
3 files changed, 70 insertions(+), 65 deletions(-)
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index f8d4c2d550073..19b4855969f53 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -180,29 +180,13 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
struct btrfs_failed_bio *fbio = repair_bbio->private;
struct btrfs_inode *inode = repair_bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- /*
- * We can not move forward the saved_iter, as it will be later
- * utilized by repair_bbio again.
- */
- struct bvec_iter saved_iter = repair_bbio->saved_iter;
- const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
- const u32 nr_steps = repair_bbio->saved_iter.bi_size / step;
int mirror = repair_bbio->mirror_num;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- phys_addr_t paddr;
- unsigned int slot = 0;
- /* Repair bbio should be eaxctly one block sized. */
+ /* Repair bbio should be exactly one block sized. */
ASSERT(repair_bbio->saved_iter.bi_size == fs_info->sectorsize);
- btrfs_bio_for_each_block(paddr, &repair_bbio->bio, &saved_iter, step) {
- ASSERT(slot < nr_steps);
- paddrs[slot] = paddr;
- slot++;
- }
-
if (repair_bbio->bio.bi_status ||
- !btrfs_data_csum_ok(repair_bbio, dev, 0, paddrs)) {
+ !btrfs_bio_data_csum_ok(repair_bbio, &repair_bbio->saved_iter, dev)) {
bio_reset(&repair_bbio->bio, NULL, REQ_OP_READ);
repair_bbio->bio.bi_iter = repair_bbio->saved_iter;
@@ -236,25 +220,21 @@ static void btrfs_end_repair_bio(struct btrfs_bio *repair_bbio,
* read succeeded to restore the redundancy.
*/
static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
- u32 bio_offset,
- phys_addr_t paddrs[],
+ const struct bvec_iter *orig_iter,
struct btrfs_failed_bio *fbio)
{
struct btrfs_inode *inode = failed_bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- const u32 sectorsize = fs_info->sectorsize;
- const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
- const u32 nr_steps = sectorsize / step;
- /*
- * For bs > ps cases, the saved_iter can be partially moved forward.
- * In that case we should round it down to the block boundary.
- */
- const u64 logical = round_down(failed_bbio->saved_iter.bi_sector << SECTOR_SHIFT,
- sectorsize);
struct btrfs_bio *repair_bbio;
struct bio *repair_bio;
+ struct bvec_iter iter = *orig_iter;
+ const u32 sectorsize = fs_info->sectorsize;
+ const u32 bio_offset = ((iter.bi_sector - failed_bbio->saved_iter.bi_sector) <<
+ SECTOR_SHIFT);
+ const u64 logical = (iter.bi_sector << SECTOR_SHIFT);
int num_copies;
int mirror;
+ u32 cur = 0;
btrfs_debug(fs_info, "repair read error: read error at %llu",
failed_bbio->file_offset + bio_offset);
@@ -275,17 +255,21 @@ static struct btrfs_failed_bio *repair_one_sector(struct btrfs_bio *failed_bbio,
atomic_inc(&fbio->repair_count);
- repair_bio = bio_alloc_bioset(NULL, nr_steps, REQ_OP_READ, GFP_NOFS,
- &btrfs_repair_bioset);
+ repair_bio = bio_alloc_bioset(NULL, max(1, sectorsize >> PAGE_SHIFT),
+ REQ_OP_READ, GFP_NOFS, &btrfs_repair_bioset);
repair_bio->bi_iter.bi_sector = logical >> SECTOR_SHIFT;
- for (int i = 0; i < nr_steps; i++) {
+ while (cur < sectorsize) {
+ struct page *page = bio_iter_page(&failed_bbio->bio, iter);
+ const u32 pg_off = bio_iter_offset(&failed_bbio->bio, iter);
+ const u32 cur_len = min(bio_iter_len(&failed_bbio->bio, iter),
+ sectorsize - cur);
int ret;
- ASSERT(offset_in_page(paddrs[i]) + step <= PAGE_SIZE);
+ ret = bio_add_page(repair_bio, page, cur_len, pg_off);
+ ASSERT(ret == cur_len);
- ret = bio_add_page(repair_bio, phys_to_page(paddrs[i]), step,
- offset_in_page(paddrs[i]));
- ASSERT(ret == step);
+ bio_advance_iter_single(&failed_bbio->bio, &iter, cur_len);
+ cur += cur_len;
}
repair_bbio = btrfs_bio(repair_bio);
@@ -303,18 +287,16 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
const u32 sectorsize = fs_info->sectorsize;
- const u32 step = min(sectorsize, PAGE_SIZE);
- const u32 nr_steps = sectorsize / step;
- struct bvec_iter *iter = &bbio->saved_iter;
+ struct bvec_iter iter;
blk_status_t status = bbio->bio.bi_status;
struct btrfs_failed_bio *fbio = NULL;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- phys_addr_t paddr;
- u32 offset = 0;
/* Read-repair requires the inode field to be set by the submitter. */
ASSERT(inode);
+ /* The original bbio should be sectorsize aligned. */
+ ASSERT(IS_ALIGNED(bbio->saved_iter.bi_size, sectorsize));
+
/*
* Hand off repair bios to the repair code as there is no upper level
* submitter for them.
@@ -327,16 +309,10 @@ static void btrfs_check_read_bio(struct btrfs_bio *bbio, struct btrfs_device *de
/* Clear the I/O error. A failed repair will reset it. */
bbio->bio.bi_status = BLK_STS_OK;
- btrfs_bio_for_each_block(paddr, &bbio->bio, iter, step) {
- paddrs[(offset / step) % nr_steps] = paddr;
- offset += step;
-
- if (IS_ALIGNED(offset, sectorsize)) {
- if (status ||
- !btrfs_data_csum_ok(bbio, dev, offset - sectorsize, paddrs))
- fbio = repair_one_sector(bbio, offset - sectorsize,
- paddrs, fbio);
- }
+ for (iter = bbio->saved_iter; iter.bi_size;
+ bio_advance_iter(&bbio->bio, &iter, sectorsize)) {
+ if (status || !btrfs_bio_data_csum_ok(bbio, &iter, dev))
+ fbio = repair_one_sector(bbio, &iter, fbio);
}
if (bbio->csum != bbio->csum_inline)
kvfree(bbio->csum);
@@ -924,7 +900,7 @@ void btrfs_submit_bbio(struct btrfs_bio *bbio, int mirror_num)
* freeing the bio.
*
* @bbio: Original bbio where the repair is needed
- * @orig_iter: Points to where the repair start is
+ * @orig_iter: Points to where the repair starts
* @length: Length of the repair write
* @mirror_num: Mirror number to write to. Must not be zero
*/
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 1082fa92c1457..171f96bdb8aa7 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -513,8 +513,8 @@ void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
const phys_addr_t paddrs[], u8 *dest);
int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
const u8 * const csum_expected);
-bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
- u32 bio_offset, const phys_addr_t paddrs[]);
+bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
+ struct btrfs_device *dev);
noinline int can_nocow_extent(struct btrfs_inode *inode, u64 offset, u64 *len,
struct btrfs_file_extent *file_extent,
bool nowait);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index aa1201fe4d7f8..f842884722081 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3551,27 +3551,31 @@ int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8
* different noncontiguous pages.
*
* @bbio: btrfs_io_bio which contains the csum
- * @dev: device the sector is on
- * @bio_offset: offset to the beginning of the bio (in bytes)
- * @paddrs: physical addresses which back the fs block
+ * @orig_iter: bvec iter pointing to the start of the block
+ * @dev: device the sector is on (optional)
*
* Check if the checksum on a data block is valid. When a checksum mismatch is
* detected, report the error and fill the corrupted range with zero.
*
* Return %true if the sector is ok or had no checksum to start with, else %false.
*/
-bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
- u32 bio_offset, const phys_addr_t paddrs[])
+bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio,
+ const struct bvec_iter *orig_iter,
+ struct btrfs_device *dev)
{
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ struct bvec_iter iter = *orig_iter;
+ struct btrfs_csum_ctx cctx;
const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
+ const u32 bio_offset = (iter.bi_sector - bbio->saved_iter.bi_sector) << SECTOR_SHIFT;
u64 file_offset = bbio->file_offset + bio_offset;
u64 end = file_offset + blocksize - 1;
u8 *csum_expected;
u8 csum[BTRFS_CSUM_SIZE];
+ u32 cur = 0;
+
+ ASSERT(iter.bi_sector >= bbio->saved_iter.bi_sector);
if (!bbio->csum)
return true;
@@ -3587,7 +3591,22 @@ bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
csum_expected = bbio->csum + (bio_offset >> fs_info->sectorsize_bits) *
fs_info->csum_size;
- btrfs_calculate_block_csum_pages(fs_info, paddrs, csum);
+ btrfs_csum_init(&cctx, fs_info->csum_type);
+ while (cur < blocksize) {
+ struct page *page = bio_iter_page(&bbio->bio, iter);
+ const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
+ const u32 cur_len = min(bio_iter_len(&bbio->bio, iter), blocksize - cur);
+ void *kaddr;
+
+ kaddr = kmap_local_page(page) + pg_off;
+ btrfs_csum_update(&cctx, kaddr, cur_len);
+ kunmap_local(kaddr);
+
+ bio_advance_iter_single(&bbio->bio, &iter, cur_len);
+ cur += cur_len;
+ }
+ btrfs_csum_final(&cctx, csum);
+
if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
goto zeroit;
return true;
@@ -3597,8 +3616,18 @@ bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,
bbio->mirror_num);
if (dev)
btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_CORRUPTION_ERRS);
- for (int i = 0; i < nr_steps; i++)
- memzero_page(phys_to_page(paddrs[i]), offset_in_page(paddrs[i]), step);
+ cur = 0;
+ iter = *orig_iter;
+ while (cur < blocksize) {
+ struct page *page = bio_iter_page(&bbio->bio, iter);
+ const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
+ const u32 cur_len = min(bio_iter_len(&bbio->bio, iter), blocksize - cur);
+
+ memzero_page(page, pg_off, cur_len);
+
+ bio_advance_iter_single(&bbio->bio, &iter, cur_len);
+ cur += cur_len;
+ }
return false;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 7.3.y 3/5] btrfs: use a shared helper to calculate data checksum for a bio
2026-11-13 14:04 ` [PATCH 7.3.y 1/5] btrfs: replace btrfs_repair_io_failure() to use bio for page iteration Sasha Levin
2026-11-13 14:04 ` [PATCH 7.3.y 2/5] btrfs: enhance btrfs_data_csum_ok() " Sasha Levin
@ 2026-11-13 14:04 ` Sasha Levin
2026-11-13 14:05 ` [PATCH 7.3.y 4/5] btrfs: remove on-stack paddrs[] array usage Sasha Levin
2026-11-13 14:05 ` [PATCH 7.3.y 5/5] btrfs: raid56: do not verify the content if there is no data checksum Sasha Levin
3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-13 14:04 UTC (permalink / raw)
To: stable; +Cc: Qu Wenruo, Boris Burkov, David Sterba, Sasha Levin
From: Qu Wenruo <wqu@suse.com>
[ Upstream commit 64978a4eced2b8f769cdfc27d68c6710e9c92627 ]
Since we are already calculating data checksum using bio interface,
extract the generation part into btrfs_csum_one_bio_block(), and use that
to replace the paddrs[] array based solution in csum_one_bio().
This will reduce 128 bytes on-stack memory usage for csum_one_bio().
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Stable-dep-of: d00b85308abe ("btrfs: raid56: do not verify the content if there is no data checksum")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/btrfs_inode.h | 2 ++
fs/btrfs/file-item.c | 19 +++++------------
fs/btrfs/inode.c | 46 +++++++++++++++++++++++++-----------------
3 files changed, 34 insertions(+), 33 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 171f96bdb8aa7..e137a99151bd0 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -515,6 +515,8 @@ int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8
const u8 * const csum_expected);
bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
struct btrfs_device *dev);
+void btrfs_csum_one_bio_block(struct btrfs_fs_info *fs_info, struct bio *bio,
+ const struct bvec_iter *orig_iter, u8 *csum);
noinline int can_nocow_extent(struct btrfs_inode *inode, u64 offset, u64 *len,
struct btrfs_file_extent *file_extent,
bool nowait);
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index cf50fd623f41a..581ca5653be93 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -801,25 +801,16 @@ static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src)
{
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
- struct bio *bio = &bbio->bio;
struct btrfs_ordered_sum *sums = bbio->sums;
- struct bvec_iter iter = *src;
- phys_addr_t paddr;
+ struct bvec_iter iter;
const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- u32 offset = 0;
int index = 0;
- btrfs_bio_for_each_block(paddr, bio, &iter, step) {
- paddrs[(offset / step) % nr_steps] = paddr;
- offset += step;
+ for (iter = *src; iter.bi_size; bio_advance_iter(&bbio->bio, &iter, blocksize)) {
+ btrfs_csum_one_bio_block(fs_info, &bbio->bio, &iter,
+ sums->sums + index);
- if (IS_ALIGNED(offset, blocksize)) {
- btrfs_calculate_block_csum_pages(fs_info, paddrs, sums->sums + index);
- index += fs_info->csum_size;
- }
+ index += fs_info->csum_size;
}
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f842884722081..c8479ffc8d1df 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3546,6 +3546,32 @@ int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8
return 0;
}
+/* Generate data checksum for a single fs block, pointed to by @orig_iter. */
+void btrfs_csum_one_bio_block(struct btrfs_fs_info *fs_info, struct bio *bio,
+ const struct bvec_iter *orig_iter, u8 *csum)
+{
+ struct btrfs_csum_ctx cctx;
+ struct bvec_iter iter = *orig_iter;
+ const u32 blocksize = fs_info->sectorsize;
+ u32 cur = 0;
+
+ btrfs_csum_init(&cctx, fs_info->csum_type);
+ while (cur < blocksize) {
+ struct page *page = bio_iter_page(bio, iter);
+ const u32 pg_off = bio_iter_offset(bio, iter);
+ const u32 cur_len = min(bio_iter_len(bio, iter), blocksize - cur);
+ void *kaddr;
+
+ kaddr = kmap_local_page(page) + pg_off;
+ btrfs_csum_update(&cctx, kaddr, cur_len);
+ kunmap_local(kaddr);
+
+ bio_advance_iter_single(bio, &iter, cur_len);
+ cur += cur_len;
+ }
+ btrfs_csum_final(&cctx, csum);
+}
+
/*
* Verify the checksum of a single data sector, which can be scattered at
* different noncontiguous pages.
@@ -3566,7 +3592,6 @@ bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio,
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct bvec_iter iter = *orig_iter;
- struct btrfs_csum_ctx cctx;
const u32 blocksize = fs_info->sectorsize;
const u32 bio_offset = (iter.bi_sector - bbio->saved_iter.bi_sector) << SECTOR_SHIFT;
u64 file_offset = bbio->file_offset + bio_offset;
@@ -3591,22 +3616,7 @@ bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio,
csum_expected = bbio->csum + (bio_offset >> fs_info->sectorsize_bits) *
fs_info->csum_size;
- btrfs_csum_init(&cctx, fs_info->csum_type);
- while (cur < blocksize) {
- struct page *page = bio_iter_page(&bbio->bio, iter);
- const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
- const u32 cur_len = min(bio_iter_len(&bbio->bio, iter), blocksize - cur);
- void *kaddr;
-
- kaddr = kmap_local_page(page) + pg_off;
- btrfs_csum_update(&cctx, kaddr, cur_len);
- kunmap_local(kaddr);
-
- bio_advance_iter_single(&bbio->bio, &iter, cur_len);
- cur += cur_len;
- }
- btrfs_csum_final(&cctx, csum);
-
+ btrfs_csum_one_bio_block(fs_info, &bbio->bio, orig_iter, csum);
if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
goto zeroit;
return true;
@@ -3616,8 +3626,6 @@ bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio,
bbio->mirror_num);
if (dev)
btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_CORRUPTION_ERRS);
- cur = 0;
- iter = *orig_iter;
while (cur < blocksize) {
struct page *page = bio_iter_page(&bbio->bio, iter);
const u32 pg_off = bio_iter_offset(&bbio->bio, iter);
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 7.3.y 4/5] btrfs: remove on-stack paddrs[] array usage
2026-11-13 14:04 ` [PATCH 7.3.y 1/5] btrfs: replace btrfs_repair_io_failure() to use bio for page iteration Sasha Levin
2026-11-13 14:04 ` [PATCH 7.3.y 2/5] btrfs: enhance btrfs_data_csum_ok() " Sasha Levin
2026-11-13 14:04 ` [PATCH 7.3.y 3/5] btrfs: use a shared helper to calculate data checksum for a bio Sasha Levin
@ 2026-11-13 14:05 ` Sasha Levin
2026-11-13 14:05 ` [PATCH 7.3.y 5/5] btrfs: raid56: do not verify the content if there is no data checksum Sasha Levin
3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-13 14:05 UTC (permalink / raw)
To: stable; +Cc: Qu Wenruo, Boris Burkov, David Sterba, Sasha Levin
From: Qu Wenruo <wqu@suse.com>
[ Upstream commit f41f4dcddea1b9eb41fc3a5491b2a8dd1f7c21ff ]
Since the bs > ps support, we have to handle cases where a data block is
inside several discontiguous pages.
Thus we need a local paddrs[] array to assemble a data block for bs > ps
cases.
However to handle all possible bs/ps combinations, we have to declare
such array using the max block size vs page size, no matter the current
block size and page size.
This adds 128 bytes on-stack memory usage for several call sites, and
also introduced several duplicated helpers to calculate checksum for a
data block:
- btrfs_calculate_block_csum_folio()
- btrfs_calculate_block_csum_pages()
- btrfs_check_block_csum()
The differences are mostly in how the data is passed.
The first one accepts a contiguous paddr range.
The second one accepts an array of paddrs[].
The last one is just a simple wrapper of the first one.
However the most common interface to iterate a data block is through
bio, and we have already converted most callers to use the bio based
interface, e.g. btrfs_bio_data_csum_ok() and btrfs_csum_one_bio_block().
Convert the remaining two call sites to address the remaining paddrs[]
usage:
- btrfs_calculate_block_csum_pages() inside verify_bio_data_sectors()
This can be switched to btrfs_csum_one_bio_block().
This removes the 128 bytes on-stack memory usage.
- btrfs_calculate_block_csum_pages() inside verify_one_sector()
This call site doesn't use on-stack memory for paddrs[], but reuses
the existing btrfs_raid_bio::bio_paddrs[] or
btrfs_raid_bio::stripe_paddrs[].
So implement a local version called calculate_block_csum_paddrs().
Now there is no fixed on-stack paddrs[] usage anymore.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Stable-dep-of: d00b85308abe ("btrfs: raid56: do not verify the content if there is no data checksum")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/btrfs_inode.h | 6 ----
fs/btrfs/inode.c | 75 ------------------------------------------
fs/btrfs/raid56.c | 46 +++++++++++++++-----------
3 files changed, 27 insertions(+), 100 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index e137a99151bd0..89e5e9c0c904f 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -507,12 +507,6 @@ static inline void btrfs_set_inode_mapping_order(struct btrfs_inode *inode)
inode->root->fs_info->block_max_order);
}
-void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddr, u8 *dest);
-void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddrs[], u8 *dest);
-int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
- const u8 * const csum_expected);
bool btrfs_bio_data_csum_ok(struct btrfs_bio *bbio, const struct bvec_iter *orig_iter,
struct btrfs_device *dev);
void btrfs_csum_one_bio_block(struct btrfs_fs_info *fs_info, struct bio *bio,
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index c8479ffc8d1df..c2a720f74d56d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3471,81 +3471,6 @@ int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered)
return btrfs_finish_one_ordered(ordered);
}
-/*
- * Calculate the checksum of an fs block at physical memory address @paddr,
- * and save the result to @dest.
- *
- * The folio containing @paddr must be large enough to contain a full fs block.
- */
-void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddr, u8 *dest)
-{
- struct folio *folio = page_folio(phys_to_page(paddr));
- const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
-
- /* The full block must be inside the folio. */
- ASSERT(offset_in_folio(folio, paddr) + blocksize <= folio_size(folio));
-
- for (int i = 0; i < nr_steps; i++) {
- u32 pindex = offset_in_folio(folio, paddr + i * step) >> PAGE_SHIFT;
-
- /*
- * For bs <= ps cases, we will only run the loop once, so the offset
- * inside the page will only added to paddrs[0].
- *
- * For bs > ps cases, the block must be page aligned, thus offset
- * inside the page will always be 0.
- */
- paddrs[i] = page_to_phys(folio_page(folio, pindex)) + offset_in_page(paddr);
- }
- return btrfs_calculate_block_csum_pages(fs_info, paddrs, dest);
-}
-
-/*
- * Calculate the checksum of a fs block backed by multiple noncontiguous pages
- * at @paddrs[] and save the result to @dest.
- *
- * The folio containing @paddr must be large enough to contain a full fs block.
- */
-void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
- const phys_addr_t paddrs[], u8 *dest)
-{
- const u32 blocksize = fs_info->sectorsize;
- const u32 step = min(blocksize, PAGE_SIZE);
- const u32 nr_steps = blocksize / step;
- struct btrfs_csum_ctx csum;
-
- btrfs_csum_init(&csum, fs_info->csum_type);
- for (int i = 0; i < nr_steps; i++) {
- const phys_addr_t paddr = paddrs[i];
- void *kaddr;
-
- ASSERT(offset_in_page(paddr) + step <= PAGE_SIZE);
- kaddr = kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
- btrfs_csum_update(&csum, kaddr, step);
- kunmap_local(kaddr);
- }
- btrfs_csum_final(&csum, dest);
-}
-
-/*
- * Verify the checksum for a single sector without any extra action that depend
- * on the type of I/O.
- *
- * @kaddr must be a properly kmapped address.
- */
-int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
- const u8 * const csum_expected)
-{
- btrfs_calculate_block_csum_folio(fs_info, paddr, csum);
- if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
- return -EIO;
- return 0;
-}
-
/* Generate data checksum for a single fs block, pointed to by @orig_iter. */
void btrfs_csum_one_bio_block(struct btrfs_fs_info *fs_info, struct bio *bio,
const struct bvec_iter *orig_iter, u8 *csum)
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 1ee52a9dcee36..a5d0ef09d92ab 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1652,12 +1652,7 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
struct bio *bio)
{
struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
- const u32 step = min(fs_info->sectorsize, PAGE_SIZE);
- const u32 nr_steps = rbio->sector_nsteps;
int total_sector_nr = get_bio_sector_nr(rbio, bio);
- u32 offset = 0;
- phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
- phys_addr_t paddr;
/* No data csum for the whole stripe, no need to verify. */
if (!rbio->csum_bitmap || !rbio->csum_buf)
@@ -1667,28 +1662,20 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio,
if (total_sector_nr >= rbio->nr_data * rbio->stripe_nsectors)
return;
- btrfs_bio_for_each_block_all(paddr, bio, step) {
+ for (struct bvec_iter iter = init_bvec_iter_for_bio(bio);
+ iter.bi_size;
+ bio_advance_iter(bio, &iter, fs_info->sectorsize), total_sector_nr++) {
u8 csum_buf[BTRFS_CSUM_SIZE];
u8 *expected_csum;
- paddrs[(offset / step) % nr_steps] = paddr;
- offset += step;
-
- /* Not yet covering the full fs block, continue to the next step. */
- if (!IS_ALIGNED(offset, fs_info->sectorsize))
- continue;
-
/* No csum for this sector, skip to the next sector. */
- if (!test_bit(total_sector_nr, rbio->csum_bitmap)) {
- total_sector_nr++;
+ if (!test_bit(total_sector_nr, rbio->csum_bitmap))
continue;
- }
expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size;
- btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
+ btrfs_csum_one_bio_block(fs_info, bio, &iter, csum_buf);
if (unlikely(memcmp(csum_buf, expected_csum, fs_info->csum_size) != 0))
set_bit(total_sector_nr, rbio->error_bitmap);
- total_sector_nr++;
}
}
@@ -1879,6 +1866,27 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc)
start_async_work(rbio, rmw_rbio_work);
}
+static void calculate_block_csum_paddrs(struct btrfs_fs_info *fs_info,
+ const phys_addr_t paddrs[], u8 *dest)
+{
+ const u32 blocksize = fs_info->sectorsize;
+ const u32 step = min(blocksize, PAGE_SIZE);
+ const u32 nr_steps = blocksize / step;
+ struct btrfs_csum_ctx csum;
+
+ btrfs_csum_init(&csum, fs_info->csum_type);
+ for (int i = 0; i < nr_steps; i++) {
+ const phys_addr_t paddr = paddrs[i];
+ void *kaddr;
+
+ ASSERT(offset_in_page(paddr) + step <= PAGE_SIZE);
+ kaddr = kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
+ btrfs_csum_update(&csum, kaddr, step);
+ kunmap_local(kaddr);
+ }
+ btrfs_csum_final(&csum, dest);
+}
+
static int verify_one_sector(struct btrfs_raid_bio *rbio,
int stripe_nr, int sector_nr)
{
@@ -1906,7 +1914,7 @@ static int verify_one_sector(struct btrfs_raid_bio *rbio,
csum_expected = rbio->csum_buf +
(stripe_nr * rbio->stripe_nsectors + sector_nr) *
fs_info->csum_size;
- btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf);
+ calculate_block_csum_paddrs(fs_info, paddrs, csum_buf);
if (unlikely(memcmp(csum_buf, csum_expected, fs_info->csum_size) != 0))
return -EIO;
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 7.3.y 5/5] btrfs: raid56: do not verify the content if there is no data checksum
2026-11-13 14:04 ` [PATCH 7.3.y 1/5] btrfs: replace btrfs_repair_io_failure() to use bio for page iteration Sasha Levin
` (2 preceding siblings ...)
2026-11-13 14:05 ` [PATCH 7.3.y 4/5] btrfs: remove on-stack paddrs[] array usage Sasha Levin
@ 2026-11-13 14:05 ` Sasha Levin
3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-11-13 14:05 UTC (permalink / raw)
To: stable; +Cc: Qu Wenruo, Bartosz Fenski, Filipe Manana, David Sterba,
Sasha Levin
From: Qu Wenruo <wqu@suse.com>
[ Upstream commit d00b85308abee300dfb7e1ac642f38bab9ec7492 ]
[BUG]
There is a bug report that fio can fail to sync the data on a degraded
RAID56 mount:
# mkfs.btrfs -m raid1 -d raid5 -f $dev1 $dev2 $dev3
# mount $dev1 $mnt
# btrfs balance start -d $mnt
# umount $mnt
# btrfs dev scan -u $dev2
# wipefs -fa $dev2
# mount -o degraded $dev1 $mnt
# fio --name=degraded-write --directory=$mnt --rw=randwrite --fdatasync=4 \
--bs=4k --size=512M --randrepeat=1
The run will fail at fsync:
fio: io_u error on file /mnt/btrfs/degraded-write.0.0: Input/output error: datasync offset=142254080, buflen=0
fio: pid=2064, err=5/file:io_u.c:2008, func=io_u error, error=Input/output error
[CAUSE]
With extra debug output, it shows the problem pretty well:
rbio=2876243968 faila=0 sectornr=0
rbio=2876243968 error=0-15 csum=2 bio=15
The first line shows the rbio's full stripe logical, and stripe/sector
number when verify_one_sector() failed.
The second line shows more detail about the rbio, the sector range [0, 16)
is corrupted, which indicates the first data stripe is corrupted, matching
the missing device.
The important part is that the csum bitmap shows the data csum is only
for sector number 2, meanwhile the failed verification is at sector
number 0.
This means we did the checksum verification even if no corresponding
checksum is populated.
[FIX]
There is already a similar check at verify_bio_data_sectors(), but
unfortunately there is no such check at verify_one_sector().
Add the missing check to skip the csum verification if there is no
corresponding data checksum.
Reported-by: Bartosz Fenski <bartosz@fenski.pl>
Link: https://lore.kernel.org/linux-btrfs/a72409af-45ef-45df-9970-2e38bcb22ea6@fenski.pl/
Fixes: 7a3150723061 ("btrfs: raid56: do data csum verification during RMW cycle")
CC: stable@vger.kernel.org
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/raid56.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index a5d0ef09d92ab..3412f43d5dec1 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1892,6 +1892,8 @@ static int verify_one_sector(struct btrfs_raid_bio *rbio,
{
struct btrfs_fs_info *fs_info = rbio->bioc->fs_info;
phys_addr_t *paddrs;
+ const unsigned int total_sector_nr =
+ stripe_nr * rbio->stripe_nsectors + sector_nr;
u8 csum_buf[BTRFS_CSUM_SIZE];
u8 *csum_expected;
@@ -1901,6 +1903,11 @@ static int verify_one_sector(struct btrfs_raid_bio *rbio,
/* No way to verify P/Q as they are not covered by data csum. */
if (stripe_nr >= rbio->nr_data)
return 0;
+
+ /* No data checksum for this sector. */
+ if (!test_bit(total_sector_nr, rbio->csum_bitmap))
+ return 0;
+
/*
* If we're rebuilding a read, we have to use pages from the
* bio list if possible.
@@ -1911,9 +1918,7 @@ static int verify_one_sector(struct btrfs_raid_bio *rbio,
paddrs = rbio_stripe_paddrs(rbio, stripe_nr, sector_nr);
}
- csum_expected = rbio->csum_buf +
- (stripe_nr * rbio->stripe_nsectors + sector_nr) *
- fs_info->csum_size;
+ csum_expected = rbio->csum_buf + total_sector_nr * fs_info->csum_size;
calculate_block_csum_paddrs(fs_info, paddrs, csum_buf);
if (unlikely(memcmp(csum_buf, csum_expected, fs_info->csum_size) != 0))
return -EIO;
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread