btrfs: introduce a data checksum checking helper
authorQu Wenruo <wqu@suse.com>
Sun, 22 May 2022 11:47:48 +0000 (13:47 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 25 Jul 2022 15:44:33 +0000 (17:44 +0200)
Although we have several data csum verification code, we never have a
function really just to verify checksum for one sector.

Function check_data_csum() do extra work for error reporting, thus it
requires a lot of extra things like file offset, bio_offset etc.

Function btrfs_verify_data_csum() is even worse, it will utilize page
checked flag, which means it can not be utilized for direct IO pages.

Here we introduce a new helper, btrfs_check_sector_csum(), which really
only accept a sector in page, and expected checksum pointer.

We use this function to implement check_data_csum(), and export it for
incoming patch.

Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
[hch: keep passing the csum array as an arguments, as the callers want
      to print it, rename per request]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/compression.c
fs/btrfs/ctree.h
fs/btrfs/inode.c

index f4564f3..6ab82e1 100644 (file)
@@ -147,12 +147,10 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio,
                                 u64 disk_start)
 {
        struct btrfs_fs_info *fs_info = inode->root->fs_info;
-       SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
        const u32 csum_size = fs_info->csum_size;
        const u32 sectorsize = fs_info->sectorsize;
        struct page *page;
        unsigned int i;
-       char *kaddr;
        u8 csum[BTRFS_CSUM_SIZE];
        struct compressed_bio *cb = bio->bi_private;
        u8 *cb_sum = cb->sums;
@@ -161,8 +159,6 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio,
            test_bit(BTRFS_FS_STATE_NO_CSUMS, &fs_info->fs_state))
                return 0;
 
-       shash->tfm = fs_info->csum_shash;
-
        for (i = 0; i < cb->nr_pages; i++) {
                u32 pg_offset;
                u32 bytes_left = PAGE_SIZE;
@@ -175,12 +171,11 @@ static int check_compressed_csum(struct btrfs_inode *inode, struct bio *bio,
                /* Hash through the page sector by sector */
                for (pg_offset = 0; pg_offset < bytes_left;
                     pg_offset += sectorsize) {
-                       kaddr = kmap_atomic(page);
-                       crypto_shash_digest(shash, kaddr + pg_offset,
-                                           sectorsize, csum);
-                       kunmap_atomic(kaddr);
+                       int ret;
 
-                       if (memcmp(&csum, cb_sum, csum_size) != 0) {
+                       ret = btrfs_check_sector_csum(fs_info, page, pg_offset,
+                                                     csum, cb_sum);
+                       if (ret) {
                                btrfs_print_data_csum_error(inode, disk_start,
                                                csum, cb_sum, cb->mirror_num);
                                if (btrfs_bio(bio)->device)
index ad31cc5..6e65778 100644 (file)
@@ -3253,6 +3253,8 @@ u64 btrfs_file_extent_end(const struct btrfs_path *path);
 /* inode.c */
 void btrfs_submit_data_bio(struct inode *inode, struct bio *bio,
                           int mirror_num, enum btrfs_compression_type compress_type);
+int btrfs_check_sector_csum(struct btrfs_fs_info *fs_info, struct page *page,
+                           u32 pgoff, u8 *csum, const u8 * const csum_expected);
 unsigned int btrfs_verify_data_csum(struct btrfs_bio *bbio,
                                    u32 bio_offset, struct page *page,
                                    u64 start, u64 end);
index 332b8f1..193931b 100644 (file)
@@ -3329,6 +3329,29 @@ void btrfs_writepage_endio_finish_ordered(struct btrfs_inode *inode,
 }
 
 /*
+ * Verify the checksum for a single sector without any extra action that depend
+ * on the type of I/O.
+ */
+int btrfs_check_sector_csum(struct btrfs_fs_info *fs_info, struct page *page,
+                           u32 pgoff, u8 *csum, const u8 * const csum_expected)
+{
+       SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
+       char *kaddr;
+
+       ASSERT(pgoff + fs_info->sectorsize <= PAGE_SIZE);
+
+       shash->tfm = fs_info->csum_shash;
+
+       kaddr = kmap_local_page(page) + pgoff;
+       crypto_shash_digest(shash, kaddr, fs_info->sectorsize, csum);
+       kunmap_local(kaddr);
+
+       if (memcmp(csum, csum_expected, fs_info->csum_size))
+               return -EIO;
+       return 0;
+}
+
+/*
  * check_data_csum - verify checksum of one sector of uncompressed data
  * @inode:     inode
  * @io_bio:    btrfs_io_bio which contains the csum
@@ -3338,14 +3361,15 @@ void btrfs_writepage_endio_finish_ordered(struct btrfs_inode *inode,
  * @start:     logical offset in the file
  *
  * The length of such check is always one sector size.
+ *
+ * When csum mismatch is detected, we will also report the error and fill the
+ * corrupted range with zero. (Thus it needs the extra parameters)
  */
 static int check_data_csum(struct inode *inode, struct btrfs_bio *bbio,
                           u32 bio_offset, struct page *page, u32 pgoff,
                           u64 start)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
-       SHASH_DESC_ON_STACK(shash, fs_info->csum_shash);
-       char *kaddr;
        u32 len = fs_info->sectorsize;
        const u32 csum_size = fs_info->csum_size;
        unsigned int offset_sectors;
@@ -3357,16 +3381,10 @@ static int check_data_csum(struct inode *inode, struct btrfs_bio *bbio,
        offset_sectors = bio_offset >> fs_info->sectorsize_bits;
        csum_expected = ((u8 *)bbio->csum) + offset_sectors * csum_size;
 
-       kaddr = kmap_atomic(page);
-       shash->tfm = fs_info->csum_shash;
-
-       crypto_shash_digest(shash, kaddr + pgoff, len, csum);
-       kunmap_atomic(kaddr);
-
-       if (memcmp(csum, csum_expected, csum_size))
+       if (btrfs_check_sector_csum(fs_info, page, pgoff, csum, csum_expected))
                goto zeroit;
-
        return 0;
+
 zeroit:
        btrfs_print_data_csum_error(BTRFS_I(inode), start, csum, csum_expected,
                                    bbio->mirror_num);