diff options
author | Omar Sandoval <osandov@fb.com> | 2015-06-19 14:52:52 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2015-08-09 10:34:26 -0400 |
commit | 4a770891d9ddf94df985ca438e78d355b8469247 (patch) | |
tree | 76ed4ed34130103c9467142d2444bada3e1150ce | |
parent | 73ff61dbe5edeb1799d7e91c8b0641f87feb75fa (diff) |
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>
-rw-r--r-- | fs/btrfs/scrub.c | 26 |
1 files changed, 22 insertions, 4 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 6bce7f2ff805..c69c75e7b841 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c | |||
@@ -2696,6 +2696,11 @@ static int scrub_extent_for_parity(struct scrub_parity *sparity, | |||
2696 | u8 csum[BTRFS_CSUM_SIZE]; | 2696 | u8 csum[BTRFS_CSUM_SIZE]; |
2697 | u32 blocksize; | 2697 | u32 blocksize; |
2698 | 2698 | ||
2699 | if (dev->missing) { | ||
2700 | scrub_parity_mark_sectors_error(sparity, logical, len); | ||
2701 | return 0; | ||
2702 | } | ||
2703 | |||
2699 | if (flags & BTRFS_EXTENT_FLAG_DATA) { | 2704 | if (flags & BTRFS_EXTENT_FLAG_DATA) { |
2700 | blocksize = sctx->sectorsize; | 2705 | blocksize = sctx->sectorsize; |
2701 | } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { | 2706 | } else if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { |
@@ -2905,6 +2910,7 @@ static noinline_for_stack int scrub_raid56_parity(struct scrub_ctx *sctx, | |||
2905 | struct btrfs_root *root = fs_info->extent_root; | 2910 | struct btrfs_root *root = fs_info->extent_root; |
2906 | struct btrfs_root *csum_root = fs_info->csum_root; | 2911 | struct btrfs_root *csum_root = fs_info->csum_root; |
2907 | struct btrfs_extent_item *extent; | 2912 | struct btrfs_extent_item *extent; |
2913 | struct btrfs_bio *bbio = NULL; | ||
2908 | u64 flags; | 2914 | u64 flags; |
2909 | int ret; | 2915 | int ret; |
2910 | int slot; | 2916 | int slot; |
@@ -2914,6 +2920,7 @@ static noinline_for_stack int scrub_raid56_parity(struct scrub_ctx *sctx, | |||
2914 | u64 extent_logical; | 2920 | u64 extent_logical; |
2915 | u64 extent_physical; | 2921 | u64 extent_physical; |
2916 | u64 extent_len; | 2922 | u64 extent_len; |
2923 | u64 mapped_length; | ||
2917 | struct btrfs_device *extent_dev; | 2924 | struct btrfs_device *extent_dev; |
2918 | struct scrub_parity *sparity; | 2925 | struct scrub_parity *sparity; |
2919 | int nsectors; | 2926 | int nsectors; |
@@ -3037,10 +3044,21 @@ again: | |||
3037 | scrub_parity_mark_sectors_data(sparity, extent_logical, | 3044 | scrub_parity_mark_sectors_data(sparity, extent_logical, |
3038 | extent_len); | 3045 | extent_len); |
3039 | 3046 | ||
3040 | scrub_remap_extent(fs_info, extent_logical, | 3047 | mapped_length = extent_len; |
3041 | extent_len, &extent_physical, | 3048 | ret = btrfs_map_block(fs_info, READ, extent_logical, |
3042 | &extent_dev, | 3049 | &mapped_length, &bbio, 0); |
3043 | &extent_mirror_num); | 3050 | if (!ret) { |
3051 | if (!bbio || mapped_length < extent_len) | ||
3052 | ret = -EIO; | ||
3053 | } | ||
3054 | if (ret) { | ||
3055 | btrfs_put_bbio(bbio); | ||
3056 | goto out; | ||
3057 | } | ||
3058 | extent_physical = bbio->stripes[0].physical; | ||
3059 | extent_mirror_num = bbio->mirror_num; | ||
3060 | extent_dev = bbio->stripes[0].dev; | ||
3061 | btrfs_put_bbio(bbio); | ||
3044 | 3062 | ||
3045 | ret = btrfs_lookup_csums_range(csum_root, | 3063 | ret = btrfs_lookup_csums_range(csum_root, |
3046 | extent_logical, | 3064 | extent_logical, |