Skip to content

Commit 42a3bc2

Browse files
adam900710kdave
authored andcommitted
btrfs: make btrfs_csum_one_bio() handle bs > ps without large folios
For bs > ps cases, all folios passed into btrfs_csum_one_bio() are ensured to be backed by large folios. But that requirement excludes features like direct IO and encoded writes. To support bs > ps without large folios, enhance btrfs_csum_one_bio() by: - Split btrfs_calculate_block_csum() into two versions * btrfs_calculate_block_csum_folio() For call sites where a fs block is always backed by a large folio. This will do extra checks on the folio size, build a paddrs[] array, and pass it into the newer btrfs_calculate_block_csum_pages() helper. For now btrfs_check_block_csum() is still using this version. * btrfs_calculate_block_csum_pages() For call sites that may hit a fs block backed by noncontiguous pages. The pages are represented by paddrs[] array, which includes the offset inside the page. This function will do the proper sub-block handling. - Make btrfs_csum_one_bio() to use btrfs_calculate_block_csum_pages() This means we will need to build a local paddrs[] array, and after filling a fs block, do the checksum calculation. Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 34bece9 commit 42a3bc2

File tree

3 files changed

+67
-25
lines changed

3 files changed

+67
-25
lines changed

fs/btrfs/btrfs_inode.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,10 @@ static inline void btrfs_set_inode_mapping_order(struct btrfs_inode *inode)
543543
#endif
544544
}
545545

546-
void btrfs_calculate_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr,
547-
u8 *dest);
546+
void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
547+
const phys_addr_t paddr, u8 *dest);
548+
void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
549+
const phys_addr_t paddrs[], u8 *dest);
548550
int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
549551
const u8 * const csum_expected);
550552
bool btrfs_data_csum_ok(struct btrfs_bio *bbio, struct btrfs_device *dev,

fs/btrfs/file-item.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -775,13 +775,22 @@ static void csum_one_bio(struct btrfs_bio *bbio, struct bvec_iter *src)
775775
struct bvec_iter iter = *src;
776776
phys_addr_t paddr;
777777
const u32 blocksize = fs_info->sectorsize;
778+
const u32 step = min(blocksize, PAGE_SIZE);
779+
const u32 nr_steps = blocksize / step;
780+
phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
781+
u32 offset = 0;
778782
int index = 0;
779783

780784
shash->tfm = fs_info->csum_shash;
781785

782-
btrfs_bio_for_each_block(paddr, bio, &iter, blocksize) {
783-
btrfs_calculate_block_csum(fs_info, paddr, sums->sums + index);
784-
index += fs_info->csum_size;
786+
btrfs_bio_for_each_block(paddr, bio, &iter, step) {
787+
paddrs[(offset / step) % nr_steps] = paddr;
788+
offset += step;
789+
790+
if (IS_ALIGNED(offset, blocksize)) {
791+
btrfs_calculate_block_csum_pages(fs_info, paddrs, sums->sums + index);
792+
index += fs_info->csum_size;
793+
}
785794
}
786795
}
787796

fs/btrfs/inode.c

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3343,36 +3343,67 @@ int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered)
33433343
return btrfs_finish_one_ordered(ordered);
33443344
}
33453345

3346-
void btrfs_calculate_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr,
3347-
u8 *dest)
3346+
/*
3347+
* Calculate the checksum of an fs block at physical memory address @paddr,
3348+
* and save the result to @dest.
3349+
*
3350+
* The folio containing @paddr must be large enough to contain a full fs block.
3351+
*/
3352+
void btrfs_calculate_block_csum_folio(struct btrfs_fs_info *fs_info,
3353+
const phys_addr_t paddr, u8 *dest)
33483354
{
33493355
struct folio *folio = page_folio(phys_to_page(paddr));
33503356
const u32 blocksize = fs_info->sectorsize;
3351-
SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
3357+
const u32 step = min(blocksize, PAGE_SIZE);
3358+
const u32 nr_steps = blocksize / step;
3359+
phys_addr_t paddrs[BTRFS_MAX_BLOCKSIZE / PAGE_SIZE];
33523360

3353-
shash->tfm = fs_info->csum_shash;
33543361
/* The full block must be inside the folio. */
33553362
ASSERT(offset_in_folio(folio, paddr) + blocksize <= folio_size(folio));
33563363

3357-
if (folio_test_partial_kmap(folio)) {
3358-
size_t cur = paddr;
3364+
for (int i = 0; i < nr_steps; i++) {
3365+
u32 pindex = offset_in_folio(folio, paddr + i * step) >> PAGE_SHIFT;
33593366

3360-
crypto_shash_init(shash);
3361-
while (cur < paddr + blocksize) {
3362-
void *kaddr;
3363-
size_t len = min(paddr + blocksize - cur,
3364-
PAGE_SIZE - offset_in_page(cur));
3367+
/*
3368+
* For bs <= ps cases, we will only run the loop once, so the offset
3369+
* inside the page will only added to paddrs[0].
3370+
*
3371+
* For bs > ps cases, the block must be page aligned, thus offset
3372+
* inside the page will always be 0.
3373+
*/
3374+
paddrs[i] = page_to_phys(folio_page(folio, pindex)) + offset_in_page(paddr);
3375+
}
3376+
return btrfs_calculate_block_csum_pages(fs_info, paddrs, dest);
3377+
}
33653378

3366-
kaddr = kmap_local_folio(folio, offset_in_folio(folio, cur));
3367-
crypto_shash_update(shash, kaddr, len);
3368-
kunmap_local(kaddr);
3369-
cur += len;
3370-
}
3371-
crypto_shash_final(shash, dest);
3372-
} else {
3373-
crypto_shash_digest(shash, phys_to_virt(paddr), blocksize, dest);
3379+
/*
3380+
* Calculate the checksum of a fs block backed by multiple noncontiguous pages
3381+
* at @paddrs[] and save the result to @dest.
3382+
*
3383+
* The folio containing @paddr must be large enough to contain a full fs block.
3384+
*/
3385+
void btrfs_calculate_block_csum_pages(struct btrfs_fs_info *fs_info,
3386+
const phys_addr_t paddrs[], u8 *dest)
3387+
{
3388+
const u32 blocksize = fs_info->sectorsize;
3389+
const u32 step = min(blocksize, PAGE_SIZE);
3390+
const u32 nr_steps = blocksize / step;
3391+
SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
3392+
3393+
shash->tfm = fs_info->csum_shash;
3394+
crypto_shash_init(shash);
3395+
for (int i = 0; i < nr_steps; i++) {
3396+
const phys_addr_t paddr = paddrs[i];
3397+
void *kaddr;
3398+
3399+
ASSERT(offset_in_page(paddr) + step <= PAGE_SIZE);
3400+
kaddr = kmap_local_page(phys_to_page(paddr)) + offset_in_page(paddr);
3401+
crypto_shash_update(shash, kaddr, step);
3402+
kunmap_local(kaddr);
33743403
}
3404+
crypto_shash_final(shash, dest);
33753405
}
3406+
33763407
/*
33773408
* Verify the checksum for a single sector without any extra action that depend
33783409
* on the type of I/O.
@@ -3382,7 +3413,7 @@ void btrfs_calculate_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr
33823413
int btrfs_check_block_csum(struct btrfs_fs_info *fs_info, phys_addr_t paddr, u8 *csum,
33833414
const u8 * const csum_expected)
33843415
{
3385-
btrfs_calculate_block_csum(fs_info, paddr, csum);
3416+
btrfs_calculate_block_csum_folio(fs_info, paddr, csum);
33863417
if (unlikely(memcmp(csum, csum_expected, fs_info->csum_size) != 0))
33873418
return -EIO;
33883419
return 0;

0 commit comments

Comments
 (0)