2014-03-31 18:34 GMT+08:00 Wang Shilong <wangsl.fnst@xxxxxxxxxxxxxx>:
> Steps to reproduce:
> # mkfs.btrfs -f /dev/sda[8-11] -m raid5 -d raid5
> # mount /dev/sda8 /mnt
> # btrfs scrub start -BR /mnt
> # echo $? <--unverified errors make return value be 3
>
> This is because we don't setup right mapping between physical
> and logical address for raid56, which makes checksum mismatch.
> But we will find everthing is fine later when rechecking using
> btrfs_map_block().
>
> This patch fixed the problem by settuping right mappings and
> we only verify data stripes' checksums.
>
> Signed-off-by: Wang Shilong <wangsl.fnst@xxxxxxxxxxxxxx>
> ---
> v1->v2:
> on the right way to fix the problem.
> ---
> fs/btrfs/scrub.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 71 insertions(+), 12 deletions(-)
>
> diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
> index db21a13..ebe4c5e 100644
> --- a/fs/btrfs/scrub.c
> +++ b/fs/btrfs/scrub.c
> @@ -2235,6 +2235,43 @@ behind_scrub_pages:
> return 0;
> }
>
> +/*
> + * Given a physical address, this will calculate it's
> + * logical offset. if this is a parity stripe, it will return
> + * the most left data stripe's logical offset.
> + *
> + * return 0 if it is a data stripe, 1 means parity stripe.
> + */
> +static int get_raid56_logic_offset(u64 physical, int num,
> + struct map_lookup *map, u64 *offset)
> +{
> + int i;
> + int j = 0;
> + u64 stripe_nr;
> + u64 tmp;
> + u64 last_offset;
> + int stripe_index;
> +
> + last_offset = (physical - map->stripes[num].physical) *
> + nr_data_stripes(map);
> + *offset = last_offset;
> + for (i = 0; i < nr_data_stripes(map); i++) {
> + *offset += i * map->stripe_len;
oops, this is obviously wrong, it should be:
> + stripe_nr = *offset;
> + do_div(stripe_nr, map->stripe_len);
> +
> + stripe_index = do_div(stripe_nr, nr_data_stripes(map));
> + tmp = stripe_nr + stripe_index;
> + stripe_index = do_div(tmp, map->num_stripes);
> + if (stripe_index == num)
> + return 0;
> + if (stripe_index < num)
> + j++;
> + }
> + *offset = last_offset + j * map->stripe_len;
> + return 1;
> +}
> +
> static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
> struct map_lookup *map,
> struct btrfs_device *scrub_dev,
> @@ -2269,16 +2306,10 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
> u64 extent_len;
> struct btrfs_device *extent_dev;
> int extent_mirror_num;
> - int stop_loop;
> -
> - if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
> - BTRFS_BLOCK_GROUP_RAID6)) {
> - if (num >= nr_data_stripes(map)) {
> - return 0;
> - }
> - }
> + int stop_loop = 0;
>
> nstripes = length;
> + physical = map->stripes[num].physical;
> offset = 0;
> do_div(nstripes, map->stripe_len);
> if (map->type & BTRFS_BLOCK_GROUP_RAID0) {
> @@ -2296,6 +2327,11 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
> } else if (map->type & BTRFS_BLOCK_GROUP_DUP) {
> increment = map->stripe_len;
> mirror_num = num % map->num_stripes + 1;
> + } else if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
> + BTRFS_BLOCK_GROUP_RAID6)) {
> + get_raid56_logic_offset(physical, num, map, &offset);
> + increment = map->stripe_len * nr_data_stripes(map);
> + mirror_num = 1;
> } else {
> increment = map->stripe_len;
> mirror_num = 1;
> @@ -2357,10 +2393,18 @@ static noinline_for_stack int scrub_stripe(struct scrub_ctx *sctx,
> * now find all extents for each stripe and scrub them
> */
> logical = base + offset;
> - physical = map->stripes[num].physical;
> logic_end = logical + increment * nstripes;
> ret = 0;
> while (logical < logic_end) {
> + /* for raid56, we skip parity stripe */
> + if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
> + BTRFS_BLOCK_GROUP_RAID6)) {
> + ret = get_raid56_logic_offset(physical, num,
> + map, &logical);
> + logical += base;
> + if (ret)
> + goto skip;
> + }
> /*
> * canceled?
> */
> @@ -2504,9 +2548,23 @@ again:
> scrub_free_csums(sctx);
> if (extent_logical + extent_len <
> key.objectid + bytes) {
> - logical += increment;
> - physical += map->stripe_len;
> -
> + if (map->type & (BTRFS_BLOCK_GROUP_RAID5 |
> + BTRFS_BLOCK_GROUP_RAID6)) {
> + /*
> + * loop until we find next data stripe
> + * or we have finished all stripes.
> + */
> + do {
> + physical += map->stripe_len;
> + ret = get_raid56_logic_offset(
> + physical, num,
> + map, &logical);
> + logical += base;
> + } while (logical < logic_end && ret);
> + } else {
> + physical += map->stripe_len;
> + logical += increment;
> + }
> if (logical < key.objectid + bytes) {
> cond_resched();
> goto again;
> @@ -2521,6 +2579,7 @@ next:
> path->slots[0]++;
> }
> btrfs_release_path(path);
> +skip:
> logical += increment;
> physical += map->stripe_len;
> spin_lock(&sctx->stat_lock);
> --
> 1.9.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html