|
@@ -2899,6 +2899,40 @@ static int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int __readpage_endio_check(struct inode *inode,
|
|
|
|
+ struct btrfs_io_bio *io_bio,
|
|
|
|
+ int icsum, struct page *page,
|
|
|
|
+ int pgoff, u64 start, size_t len)
|
|
|
|
+{
|
|
|
|
+ char *kaddr;
|
|
|
|
+ u32 csum_expected;
|
|
|
|
+ u32 csum = ~(u32)0;
|
|
|
|
+ static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
|
|
|
|
+ DEFAULT_RATELIMIT_BURST);
|
|
|
|
+
|
|
|
|
+ csum_expected = *(((u32 *)io_bio->csum) + icsum);
|
|
|
|
+
|
|
|
|
+ kaddr = kmap_atomic(page);
|
|
|
|
+ csum = btrfs_csum_data(kaddr + pgoff, csum, len);
|
|
|
|
+ btrfs_csum_final(csum, (char *)&csum);
|
|
|
|
+ if (csum != csum_expected)
|
|
|
|
+ goto zeroit;
|
|
|
|
+
|
|
|
|
+ kunmap_atomic(kaddr);
|
|
|
|
+ return 0;
|
|
|
|
+zeroit:
|
|
|
|
+ if (__ratelimit(&_rs))
|
|
|
|
+ btrfs_info(BTRFS_I(inode)->root->fs_info,
|
|
|
|
+ "csum failed ino %llu off %llu csum %u expected csum %u",
|
|
|
|
+ btrfs_ino(inode), start, csum, csum_expected);
|
|
|
|
+ memset(kaddr + pgoff, 1, len);
|
|
|
|
+ flush_dcache_page(page);
|
|
|
|
+ kunmap_atomic(kaddr);
|
|
|
|
+ if (csum_expected == 0)
|
|
|
|
+ return 0;
|
|
|
|
+ return -EIO;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* when reads are done, we need to check csums to verify the data is correct
|
|
* when reads are done, we need to check csums to verify the data is correct
|
|
* if there's a match, we allow the bio to finish. If not, the code in
|
|
* if there's a match, we allow the bio to finish. If not, the code in
|
|
@@ -2911,20 +2945,15 @@ static int btrfs_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
|
|
size_t offset = start - page_offset(page);
|
|
size_t offset = start - page_offset(page);
|
|
struct inode *inode = page->mapping->host;
|
|
struct inode *inode = page->mapping->host;
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
|
- char *kaddr;
|
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
- u32 csum_expected;
|
|
|
|
- u32 csum = ~(u32)0;
|
|
|
|
- static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
|
|
|
|
- DEFAULT_RATELIMIT_BURST);
|
|
|
|
|
|
|
|
if (PageChecked(page)) {
|
|
if (PageChecked(page)) {
|
|
ClearPageChecked(page);
|
|
ClearPageChecked(page);
|
|
- goto good;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
|
|
if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
|
|
- goto good;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID &&
|
|
if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID &&
|
|
test_range_bit(io_tree, start, end, EXTENT_NODATASUM, 1, NULL)) {
|
|
test_range_bit(io_tree, start, end, EXTENT_NODATASUM, 1, NULL)) {
|
|
@@ -2934,28 +2963,8 @@ static int btrfs_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
|
|
}
|
|
}
|
|
|
|
|
|
phy_offset >>= inode->i_sb->s_blocksize_bits;
|
|
phy_offset >>= inode->i_sb->s_blocksize_bits;
|
|
- csum_expected = *(((u32 *)io_bio->csum) + phy_offset);
|
|
|
|
-
|
|
|
|
- kaddr = kmap_atomic(page);
|
|
|
|
- csum = btrfs_csum_data(kaddr + offset, csum, end - start + 1);
|
|
|
|
- btrfs_csum_final(csum, (char *)&csum);
|
|
|
|
- if (csum != csum_expected)
|
|
|
|
- goto zeroit;
|
|
|
|
-
|
|
|
|
- kunmap_atomic(kaddr);
|
|
|
|
-good:
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
-zeroit:
|
|
|
|
- if (__ratelimit(&_rs))
|
|
|
|
- btrfs_info(root->fs_info, "csum failed ino %llu off %llu csum %u expected csum %u",
|
|
|
|
- btrfs_ino(page->mapping->host), start, csum, csum_expected);
|
|
|
|
- memset(kaddr + offset, 1, end - start + 1);
|
|
|
|
- flush_dcache_page(page);
|
|
|
|
- kunmap_atomic(kaddr);
|
|
|
|
- if (csum_expected == 0)
|
|
|
|
- return 0;
|
|
|
|
- return -EIO;
|
|
|
|
|
|
+ return __readpage_endio_check(inode, io_bio, phy_offset, page, offset,
|
|
|
|
+ start, (size_t)(end - start + 1));
|
|
}
|
|
}
|
|
|
|
|
|
struct delayed_iput {
|
|
struct delayed_iput {
|
|
@@ -7238,41 +7247,24 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
|
|
struct btrfs_dio_private *dip = bio->bi_private;
|
|
struct btrfs_dio_private *dip = bio->bi_private;
|
|
struct bio_vec *bvec;
|
|
struct bio_vec *bvec;
|
|
struct inode *inode = dip->inode;
|
|
struct inode *inode = dip->inode;
|
|
- struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
struct bio *dio_bio;
|
|
struct bio *dio_bio;
|
|
struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
|
|
struct btrfs_io_bio *io_bio = btrfs_io_bio(bio);
|
|
- u32 *csums = (u32 *)io_bio->csum;
|
|
|
|
u64 start;
|
|
u64 start;
|
|
|
|
+ int ret;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
|
|
+ if (err || (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
|
|
|
|
+ goto skip_checksum;
|
|
|
|
+
|
|
start = dip->logical_offset;
|
|
start = dip->logical_offset;
|
|
bio_for_each_segment_all(bvec, bio, i) {
|
|
bio_for_each_segment_all(bvec, bio, i) {
|
|
- if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
|
|
|
|
- struct page *page = bvec->bv_page;
|
|
|
|
- char *kaddr;
|
|
|
|
- u32 csum = ~(u32)0;
|
|
|
|
- unsigned long flags;
|
|
|
|
-
|
|
|
|
- local_irq_save(flags);
|
|
|
|
- kaddr = kmap_atomic(page);
|
|
|
|
- csum = btrfs_csum_data(kaddr + bvec->bv_offset,
|
|
|
|
- csum, bvec->bv_len);
|
|
|
|
- btrfs_csum_final(csum, (char *)&csum);
|
|
|
|
- kunmap_atomic(kaddr);
|
|
|
|
- local_irq_restore(flags);
|
|
|
|
-
|
|
|
|
- flush_dcache_page(bvec->bv_page);
|
|
|
|
- if (csum != csums[i]) {
|
|
|
|
- btrfs_err(root->fs_info, "csum failed ino %llu off %llu csum %u expected csum %u",
|
|
|
|
- btrfs_ino(inode), start, csum,
|
|
|
|
- csums[i]);
|
|
|
|
- err = -EIO;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ ret = __readpage_endio_check(inode, io_bio, i, bvec->bv_page,
|
|
|
|
+ 0, start, bvec->bv_len);
|
|
|
|
+ if (ret)
|
|
|
|
+ err = -EIO;
|
|
start += bvec->bv_len;
|
|
start += bvec->bv_len;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+skip_checksum:
|
|
unlock_extent(&BTRFS_I(inode)->io_tree, dip->logical_offset,
|
|
unlock_extent(&BTRFS_I(inode)->io_tree, dip->logical_offset,
|
|
dip->logical_offset + dip->bytes - 1);
|
|
dip->logical_offset + dip->bytes - 1);
|
|
dio_bio = dip->dio_bio;
|
|
dio_bio = dip->dio_bio;
|