diff mbox series

[12/14] btrfs: scrub: add RAID56 support for queue_scrub_logical_stripes()

Message ID 9e1666311b1e944fd666e97f77af59b198717676.1688368617.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: scrub: introduce SCRUB_LOGICAL flag | expand

Commit Message

Qu Wenruo July 3, 2023, 7:32 a.m. UTC
There are several factors that needs to be considered for RAID56 full
stripe:

- Empty data stripes
  We still need those empty data stripes, as they may be involved in the
  P/Q recovery.

- P/Q stripes
  We need special handling for P/Q stripes.

So this patch introduce a new helper, queue_raid56_full_stripes() to
handle those special requirements.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/scrub.c | 108 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 105 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 21b3fa55d0f0..f37b9fd30595 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -105,7 +105,7 @@  struct scrub_stripe {
 	u64 logical;
 	u64 physical;
 
-	u16 mirror_num;
+	s16 mirror_num;
 
 	/* Should be BTRFS_STRIPE_LEN / sectorsize. */
 	u16 nr_sectors;
@@ -3147,15 +3147,109 @@  static void scrub_copy_stripe(const struct scrub_stripe *src,
 	set_bit(SCRUB_STRIPE_FLAG_INITIALIZED, &dst->state);
 }
 
+static int queue_raid56_full_stripes(struct scrub_ctx *sctx,
+			struct btrfs_block_group *bg, struct map_lookup *map,
+			u64 logical)
+{
+	const int num_stripes = map->num_stripes;
+	const int old_cur = sctx->cur_stripe;
+	const int data_stripes = nr_data_stripes(map);
+	struct btrfs_fs_info *fs_info = sctx->fs_info;
+	struct btrfs_root *extent_root = btrfs_extent_root(fs_info, bg->start);
+	struct btrfs_path path = { 0 };
+	unsigned long extents_bitmap = 0;
+	u64 extent_start;
+	u64 extent_len;
+	u64 extent_flags;
+	u64 extent_gen;
+	u64 full_stripe_start;
+	u32 full_stripe_nr;
+	int ret;
+
+	path.search_commit_root = 1;
+	path.skip_locking = 1;
+
+	ret = find_first_extent_item(extent_root, &path, logical,
+				     bg->start + bg->length - logical);
+	/* Error or no more extent found. */
+	if (ret)
+		return ret;
+	get_extent_info(&path, &extent_start, &extent_len, &extent_flags,
+			&extent_gen);
+	btrfs_release_path(&path);
+
+	full_stripe_nr = rounddown((extent_start - bg->start) >>
+				   BTRFS_STRIPE_LEN_SHIFT, data_stripes);
+	full_stripe_start = btrfs_stripe_nr_to_offset(full_stripe_nr) + bg->start;
+
+	for (int i = 0; i < data_stripes; i++) {
+		struct scrub_stripe *stripe = &sctx->stripes[old_cur + i];
+		u64 cur = full_stripe_start + btrfs_stripe_nr_to_offset(i);
+
+		scrub_reset_stripe(stripe);
+		ret = scrub_find_fill_first_stripe(bg, NULL, 0, 1, cur,
+						   BTRFS_STRIPE_LEN, stripe);
+		if (ret < 0)
+			return ret;
+		/*
+		 * No extent in the data stripe, we need to fill the info
+		 * manually.
+		 */
+		if (ret > 0) {
+			stripe->logical = cur;
+			stripe->physical = 0;
+			stripe->dev = NULL;
+			stripe->bg = bg;
+			stripe->mirror_num = 1;
+			set_bit(SCRUB_STRIPE_FLAG_INITIALIZED, &stripe->state);
+			ret = 0;
+		}
+		bitmap_or(&extents_bitmap, &extents_bitmap,
+			  &stripe->extent_sector_bitmap, stripe->nr_sectors);
+	}
+
+	/* Manually fill the P/Q stripes */
+	for (int i = 0; i < num_stripes - data_stripes; i++) {
+		struct scrub_stripe *stripe =
+			&sctx->stripes[old_cur + data_stripes + i];
+		scrub_reset_stripe(stripe);
+		/*
+		 * Use the logical of the last data stripes, so the caller
+		 * knows exactly where the next logical should start, no matter
+		 * if it's RAID56 or not.
+		 */
+		stripe->logical = full_stripe_start +
+				btrfs_stripe_nr_to_offset(data_stripes - 1);
+		stripe->dev = NULL;
+		stripe->bg = bg;
+		stripe->mirror_num = -1 - i;
+		bitmap_copy(&stripe->extent_sector_bitmap, &extents_bitmap,
+			    stripe->nr_sectors);
+		/*
+		 * For parity stripes, they don't need any csum checks,
+		 * so mark them as nocsum data.
+		 */
+		for (int sector = 0; sector < stripe->nr_sectors; sector++) {
+			stripe->sectors[sector].is_metadata = false;
+			stripe->sectors[sector].csum = NULL;
+		}
+		set_bit(SCRUB_STRIPE_FLAG_INITIALIZED, &stripe->state);
+	}
+	sctx->cur_stripe += num_stripes;
+	return 0;
+}
+
 /*
  * Unlike queue_scrub_stripe() which only queue one stripe, this queue all
  * mirrors for non-RAID56 profiles, or the full stripe for RAID56.
  */
 static int queue_scrub_logical_stripes(struct scrub_ctx *sctx,
-			struct btrfs_block_group *bg, u64 logical)
+			struct btrfs_block_group *bg, struct map_lookup *map,
+			u64 logical)
 {
 	const int nr_copies = btrfs_bg_type_to_factor(bg->flags);
 	const int old_cur = sctx->cur_stripe;
+	bool is_raid56 = bg->flags & BTRFS_BLOCK_GROUP_RAID56_MASK;
 	struct scrub_stripe *stripe;
 	int ret;
 
@@ -3168,6 +3262,14 @@  static int queue_scrub_logical_stripes(struct scrub_ctx *sctx,
 			return ret;
 	}
 
+	/*
+	 * For RAID56, we need to queue the full stripe. If there is some data
+	 * stripes containing no sectors, we still need to queue those empty
+	 * stripes for later recovery and P/Q verification.
+	 */
+	if (is_raid56)
+		return queue_raid56_full_stripes(sctx, bg, map, logical);
+
 	stripe = &sctx->stripes[old_cur];
 
 	scrub_reset_stripe(stripe);
@@ -3250,7 +3352,7 @@  static int scrub_logical_one_chunk(struct scrub_ctx *sctx,
 		}
 		spin_unlock(&bg->lock);
 
-		ret = queue_scrub_logical_stripes(sctx, bg, cur);
+		ret = queue_scrub_logical_stripes(sctx, bg, em->map_lookup, cur);
 		if (ret > 0) {
 			ret = 0;
 			break;