summaryrefslogtreecommitdiff
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2025-09-02 10:51:25 +0930
committerDavid Sterba <dsterba@suse.com>2025-09-23 08:49:16 +0200
commit35aff706dccbc51d30df6fedba76a746a1322839 (patch)
treefd64b7c52dc925b7a7987030182d2d1643c5240f /fs/btrfs/inode.c
parent2ccfaf73690960e800c2dd7debea559de4b58010 (diff)
btrfs: concentrate highmem handling for data verification
Currently for btrfs checksum verification, we do it in the following pattern: kaddr = kmap_local_*(); ret = btrfs_check_csum_csum(kaddr); kunmap_local(kaddr); It's OK for now, but it's still not following the patterns of helpers inside linux/highmem.h, which never requires a virt memory address. In those highmem helpers, they mostly accept a folio, some offset/length inside the folio, and in the implementation they check if the folio needs partial kmap, and do the handling. Inspired by those formal highmem helpers, enhance the highmem handling of data checksum verification by: - Rename btrfs_check_sector_csum() to btrfs_check_block_csum() To follow the more common term "block" used in all other major filesystems. - Pass a physical address into btrfs_check_block_csum() and btrfs_data_csum_ok() The physical address is always available even for a highmem page. Since it's page frame number << PAGE_SHIFT + offset in page. And with that physical address, we can grab the folio covering the page, and do extra checks to ensure it covers at least one block. This also allows us to do the kmap inside btrfs_check_block_csum(). This means all the extra HIGHMEM handling will be concentrated into btrfs_check_block_csum(), and no callers will need to bother highmem by themselves. - Properly zero out the block if csum mismatch Since btrfs_data_csum_ok() only got a paddr, we can not and should not use memzero_bvec(), which only accepts single page bvec. Instead use paddr to grab the folio and call folio_zero_range() Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r--fs/btrfs/inode.c47
1 files changed, 33 insertions, 14 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index dd503dba33cf..98877535f213 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3334,13 +3334,35 @@ int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered)
*
* @kaddr must be a properly kmapped address.
*/
-int btrfs_check_sector_csum(struct btrfs_fs_info *fs_info, void *kaddr, u8 *csum,
- const u8 * const csum_expected)
+int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
+ const u8 * const csum_expected)
{
+ struct folio *folio = page_folio(phys_to_page(paddr));
+ const u32 blocksize = fs_info->sectorsize;
SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
shash->tfm = fs_info->csum_shash;
- crypto_shash_digest(shash, kaddr, fs_info->sectorsize, csum);
+ /* The full block must be inside the folio. */
+ ASSERT(offset_in_folio(folio, paddr) + blocksize <= folio_size(folio));
+
+ if (folio_test_partial_kmap(folio)) {
+ size_t cur = paddr;
+
+ crypto_shash_init(shash);
+ while (cur < paddr + blocksize) {
+ void *kaddr;
+ size_t len = min(paddr + blocksize - cur,
+ PAGE_SIZE - offset_in_page(cur));
+
+ kaddr = kmap_local_folio(folio, offset_in_folio(folio, cur));
+ crypto_shash_update(shash, kaddr, len);
+ kunmap_local(kaddr);
+ cur += len;
+ }
+ crypto_shash_final(shash, csum);
+ } else {
+ crypto_shash_digest(shash, phys_to_virt(paddr), blocksize, csum);
+ }
if (memcmp(csum, csum_expected, fs_info->csum_size))
return -EIO;
@@ -3361,17 +3383,16 @@ int btrfs_check_sector_csum(struct btrfs_fs_info *fs_info, void *kaddr, u8 *csum
* 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, struct bio_vec *bv)
+ u32 bio_offset, phys_addr_t paddr)
{
struct btrfs_inode *inode = bbio->inode;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ const u32 blocksize = fs_info->sectorsize;
+ struct folio *folio;
u64 file_offset = bbio->file_offset + bio_offset;
- u64 end = file_offset + bv->bv_len - 1;
+ u64 end = file_offset + blocksize - 1;
u8 *csum_expected;
u8 csum[BTRFS_CSUM_SIZE];
- void *kaddr;
-
- ASSERT(bv->bv_len == fs_info->sectorsize);
if (!bbio->csum)
return true;
@@ -3387,12 +3408,8 @@ 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;
- kaddr = bvec_kmap_local(bv);
- if (btrfs_check_sector_csum(fs_info, kaddr, csum, csum_expected)) {
- kunmap_local(kaddr);
+ if (btrfs_check_block_csum(fs_info, paddr, csum, csum_expected))
goto zeroit;
- }
- kunmap_local(kaddr);
return true;
zeroit:
@@ -3400,7 +3417,9 @@ zeroit:
bbio->mirror_num);
if (dev)
btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_CORRUPTION_ERRS);
- memzero_bvec(bv);
+ folio = page_folio(phys_to_page(paddr));
+ ASSERT(offset_in_folio(folio, paddr) + blocksize <= folio_size(folio));
+ folio_zero_range(folio, offset_in_folio(folio, paddr), blocksize);
return false;
}