mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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…
	
		Reference in a new issue