@@ -490,35 +490,13 @@ static int btree_read_extent_buffer_pages(struct extent_buffer *eb,
return ret;
}
-/*
- * checksum a dirty tree block before IO. This has extra checks to make sure
- * we only fill in the checksum field in the first page of a multi-page block
- */
-
-static int csum_dirty_buffer(struct btrfs_fs_info *fs_info, struct bio_vec *bvec)
+static int csum_one_extent_buffer(struct extent_buffer *eb)
{
- struct extent_buffer *eb;
- struct page *page = bvec->bv_page;
- u64 start = page_offset(page);
- u64 found_start;
+ struct btrfs_fs_info *fs_info = eb->fs_info;
u8 result[BTRFS_CSUM_SIZE];
u16 csum_size = btrfs_super_csum_size(fs_info->super_copy);
int ret;
- eb = (struct extent_buffer *)page->private;
- if (page != eb->pages[0])
- return 0;
-
- found_start = btrfs_header_bytenr(eb);
- /*
- * Please do not consolidate these warnings into a single if.
- * It is useful to know what went wrong.
- */
- if (WARN_ON(found_start != start))
- return -EUCLEAN;
- if (WARN_ON(!PageUptodate(page)))
- return -EUCLEAN;
-
ASSERT(memcmp_extent_buffer(eb, fs_info->fs_devices->metadata_uuid,
offsetof(struct btrfs_header, fsid),
BTRFS_FSID_SIZE) == 0);
@@ -543,6 +521,83 @@ static int csum_dirty_buffer(struct btrfs_fs_info *fs_info, struct bio_vec *bvec
return 0;
}
+/*
+ * Do all the csum calculation and extra sanity checks on all extent
+ * buffers in the bvec.
+ */
+static int csum_dirty_subpage_buffers(struct btrfs_fs_info *fs_info,
+ struct bio_vec *bvec)
+{
+ struct page *page = bvec->bv_page;
+ u64 page_start = page_offset(page);
+ u64 start = page_start + bvec->bv_offset;
+ u64 end = start + bvec->bv_len - 1;
+ u64 cur = start;
+ int ret = 0;
+
+ while (cur <= end) {
+ struct extent_io_tree *io_tree = info_to_btree_io_tree(fs_info);
+ struct extent_buffer *eb;
+
+ ret = btrfs_find_first_subpage_eb(fs_info, &eb, cur, end, 0);
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+
+ /*
+ * Here we can't use PageUptodate() to check the status.
+ * As one page is uptodate only when all its extent buffers
+ * are uptodate, and no holes between them.
+ * So here we use EXTENT_UPTODATE bit to make sure the exntent
+ * buffer is uptodate.
+ */
+ if (WARN_ON(test_range_bit(io_tree, eb->start,
+ eb->start + eb->len - 1, EXTENT_UPTODATE, 1,
+ NULL) == 0))
+ return -EUCLEAN;
+ if (WARN_ON(cur != btrfs_header_bytenr(eb)))
+ return -EUCLEAN;
+
+ ret = csum_one_extent_buffer(eb);
+ if (ret < 0)
+ return ret;
+ cur = eb->start + eb->len;
+ }
+ return ret;
+}
+
+/*
+ * checksum a dirty tree block before IO. This has extra checks to make sure
+ * we only fill in the checksum field in the first page of a multi-page block
+ */
+static int csum_dirty_buffer(struct btrfs_fs_info *fs_info, struct bio_vec *bvec)
+{
+ struct extent_buffer *eb;
+ struct page *page = bvec->bv_page;
+ u64 start = page_offset(page) + bvec->bv_offset;
+ u64 found_start;
+
+ if (btrfs_is_subpage(fs_info))
+ return csum_dirty_subpage_buffers(fs_info, bvec);
+
+ eb = (struct extent_buffer *)page->private;
+ if (page != eb->pages[0])
+ return 0;
+
+ found_start = btrfs_header_bytenr(eb);
+ /*
+ * Please do not consolidate these warnings into a single if.
+ * It is useful to know what went wrong.
+ */
+ if (WARN_ON(found_start != start))
+ return -EUCLEAN;
+ if (WARN_ON(!PageUptodate(page)))
+ return -EUCLEAN;
+
+ return csum_one_extent_buffer(eb);
+}
+
static int check_tree_block_fsid(struct extent_buffer *eb)
{
struct btrfs_fs_info *fs_info = eb->fs_info;
Add a new helper, csum_dirty_subpage_buffers(), to iterate through all possible extent buffers in one bvec. Also extract the code to calculate csum for one extent buffer into csum_one_extent_buffer(), so that both the existing csum_dirty_buffer() and the new csum_dirty_subpage_buffers() can reuse the same routine. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/disk-io.c | 103 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 24 deletions(-)