mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
Btrfs: fix parity scrub of RAID 5/6 with missing device
When testing the previous patch, Zhao Lei reported a similar bug when attempting to scrub a degraded RAID 5/6 filesystem with a missing device, leading to NULL pointer dereferences from the RAID 5/6 parity scrubbing code. The first cause was the same as in the previous patch: attempting to call bio_add_page() on a missing block device. To fix this, scrub_extent_for_parity() can just mark the sectors on the missing device as errors instead of attempting to read from it. Additionally, the code uses scrub_remap_extent() to map the extent of the corresponding data stripe, but the extent wasn't already mapped. If scrub_remap_extent() finds a missing block device, it doesn't initialize extent_dev, so we're left with a NULL struct btrfs_device. The solution is to use btrfs_map_block() directly. Reported-by: Zhao Lei <zhaolei@cn.fujitsu.com> Signed-off-by: Omar Sandoval <osandov@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
parent
73ff61dbe5
commit
4a770891d9
1 changed files with 22 additions and 4 deletions
|
@ -2696,6 +2696,11 @@ static int scrub_extent_for_parity(struct scrub_parity *sparity,
|
|||
u8 csum[BTRFS_CSUM_SIZE];
|
||||
u32 blocksize;
|
||||
|
||||
if (dev->missing) {
|
||||
scrub_parity_mark_sectors_error(sparity, logical, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags & BTRFS_EXTENT_FLAG_DATA) {
|
||||
blocksize = sctx->sectorsize;
|
||||
} else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
|
||||
|
@ -2905,6 +2910,7 @@ static noinline_for_stack int scrub_raid56_parity(struct scrub_ctx *sctx,
|
|||
struct btrfs_root *root = fs_info->extent_root;
|
||||
struct btrfs_root *csum_root = fs_info->csum_root;
|
||||
struct btrfs_extent_item *extent;
|
||||
struct btrfs_bio *bbio = NULL;
|
||||
u64 flags;
|
||||
int ret;
|
||||
int slot;
|
||||
|
@ -2914,6 +2920,7 @@ static noinline_for_stack int scrub_raid56_parity(struct scrub_ctx *sctx,
|
|||
u64 extent_logical;
|
||||
u64 extent_physical;
|
||||
u64 extent_len;
|
||||
u64 mapped_length;
|
||||
struct btrfs_device *extent_dev;
|
||||
struct scrub_parity *sparity;
|
||||
int nsectors;
|
||||
|
@ -3037,10 +3044,21 @@ static noinline_for_stack int scrub_raid56_parity(struct scrub_ctx *sctx,
|
|||
scrub_parity_mark_sectors_data(sparity, extent_logical,
|
||||
extent_len);
|
||||
|
||||
scrub_remap_extent(fs_info, extent_logical,
|
||||
extent_len, &extent_physical,
|
||||
&extent_dev,
|
||||
&extent_mirror_num);
|
||||
mapped_length = extent_len;
|
||||
ret = btrfs_map_block(fs_info, READ, extent_logical,
|
||||
&mapped_length, &bbio, 0);
|
||||
if (!ret) {
|
||||
if (!bbio || mapped_length < extent_len)
|
||||
ret = -EIO;
|
||||
}
|
||||
if (ret) {
|
||||
btrfs_put_bbio(bbio);
|
||||
goto out;
|
||||
}
|
||||
extent_physical = bbio->stripes[0].physical;
|
||||
extent_mirror_num = bbio->mirror_num;
|
||||
extent_dev = bbio->stripes[0].dev;
|
||||
btrfs_put_bbio(bbio);
|
||||
|
||||
ret = btrfs_lookup_csums_range(csum_root,
|
||||
extent_logical,
|
||||
|
|
Loading…
Reference in a new issue