btrfs: handle csum lookup errors properly on reads
Currently any error we get while trying to lookup csums during reads shows up as a missing csum, and then on the read completion side we print an error saying there was a csum mismatch and we increase the device corruption count. However we could have gotten an EIO from the lookup. We could also be inside of a memory constrained container and gotten a ENOMEM while trying to do the read. In either case we don't want to make this look like a file system corruption problem, we want to make it look like the actual error it is. Capture any negative value, convert it to the appropriate blk_status_t, free the csum array if we have one and bail. Note: a possible improvement would be to make the relocation code look up the owning inode and see if it's marked as NODATASUM and set EXTENT_NODATASUM there, that way if there's corruption and there isn't a checksum when we want it we can fail here rather than later. Signed-off-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
03ddb19d2e
commit
1784b7d502
1 changed files with 22 additions and 14 deletions
|
@ -368,6 +368,7 @@ blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u8 *dst
|
||||||
{
|
{
|
||||||
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
|
||||||
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
|
||||||
|
struct btrfs_bio *bbio = NULL;
|
||||||
struct btrfs_path *path;
|
struct btrfs_path *path;
|
||||||
const u32 sectorsize = fs_info->sectorsize;
|
const u32 sectorsize = fs_info->sectorsize;
|
||||||
const u32 csum_size = fs_info->csum_size;
|
const u32 csum_size = fs_info->csum_size;
|
||||||
|
@ -377,6 +378,7 @@ blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u8 *dst
|
||||||
u8 *csum;
|
u8 *csum;
|
||||||
const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits;
|
const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
blk_status_t ret = BLK_STS_OK;
|
||||||
|
|
||||||
if ((BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM) ||
|
if ((BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM) ||
|
||||||
test_bit(BTRFS_FS_STATE_NO_CSUMS, &fs_info->fs_state))
|
test_bit(BTRFS_FS_STATE_NO_CSUMS, &fs_info->fs_state))
|
||||||
|
@ -400,7 +402,7 @@ blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u8 *dst
|
||||||
return BLK_STS_RESOURCE;
|
return BLK_STS_RESOURCE;
|
||||||
|
|
||||||
if (!dst) {
|
if (!dst) {
|
||||||
struct btrfs_bio *bbio = btrfs_bio(bio);
|
bbio = btrfs_bio(bio);
|
||||||
|
|
||||||
if (nblocks * csum_size > BTRFS_BIO_INLINE_CSUM_SIZE) {
|
if (nblocks * csum_size > BTRFS_BIO_INLINE_CSUM_SIZE) {
|
||||||
bbio->csum = kmalloc_array(nblocks, csum_size, GFP_NOFS);
|
bbio->csum = kmalloc_array(nblocks, csum_size, GFP_NOFS);
|
||||||
|
@ -456,21 +458,27 @@ blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u8 *dst
|
||||||
|
|
||||||
count = search_csum_tree(fs_info, path, cur_disk_bytenr,
|
count = search_csum_tree(fs_info, path, cur_disk_bytenr,
|
||||||
search_len, csum_dst);
|
search_len, csum_dst);
|
||||||
if (count <= 0) {
|
if (count < 0) {
|
||||||
/*
|
ret = errno_to_blk_status(count);
|
||||||
* Either we hit a critical error or we didn't find
|
if (bbio)
|
||||||
* the csum.
|
btrfs_bio_free_csum(bbio);
|
||||||
* Either way, we put zero into the csums dst, and skip
|
break;
|
||||||
* to the next sector.
|
}
|
||||||
*/
|
|
||||||
|
/*
|
||||||
|
* We didn't find a csum for this range. We need to make sure
|
||||||
|
* we complain loudly about this, because we are not NODATASUM.
|
||||||
|
*
|
||||||
|
* However for the DATA_RELOC inode we could potentially be
|
||||||
|
* relocating data extents for a NODATASUM inode, so the inode
|
||||||
|
* itself won't be marked with NODATASUM, but the extent we're
|
||||||
|
* copying is in fact NODATASUM. If we don't find a csum we
|
||||||
|
* assume this is the case.
|
||||||
|
*/
|
||||||
|
if (count == 0) {
|
||||||
memset(csum_dst, 0, csum_size);
|
memset(csum_dst, 0, csum_size);
|
||||||
count = 1;
|
count = 1;
|
||||||
|
|
||||||
/*
|
|
||||||
* For data reloc inode, we need to mark the range
|
|
||||||
* NODATASUM so that balance won't report false csum
|
|
||||||
* error.
|
|
||||||
*/
|
|
||||||
if (BTRFS_I(inode)->root->root_key.objectid ==
|
if (BTRFS_I(inode)->root->root_key.objectid ==
|
||||||
BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
BTRFS_DATA_RELOC_TREE_OBJECTID) {
|
||||||
u64 file_offset;
|
u64 file_offset;
|
||||||
|
@ -491,7 +499,7 @@ blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u8 *dst
|
||||||
}
|
}
|
||||||
|
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
return BLK_STS_OK;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
|
int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
|
||||||
|
|
Loading…
Add table
Reference in a new issue