aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOmar Sandoval <osandov@fb.com>2015-06-19 14:52:52 -0400
committerChris Mason <clm@fb.com>2015-08-09 10:34:26 -0400
commit4a770891d9ddf94df985ca438e78d355b8469247 (patch)
tree76ed4ed34130103c9467142d2444bada3e1150ce
parent73ff61dbe5edeb1799d7e91c8b0641f87feb75fa (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.c26
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,