diff mbox series

[PoC,05/11] btrfs: scrub: add a writeback helper for scrub2_stripe

Message ID 1e92cdad3b60464ed6f1cf4c0a24cac7d270e3ef.1670314744.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: scrub: rework to get rid of the complex bio formshaping | expand

Commit Message

Qu Wenruo Dec. 6, 2022, 8:23 a.m. UTC
Add a new helper, scrub2_writeback_sectors(), to writeback specified
sectors of a scrub2_stripe.

Unlike the read path, writeback can only submit writes for the repair
sectors, no longer in a BTRFS_STRIPE_LEN size.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/scrub.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/scrub.h |   2 +
 2 files changed, 111 insertions(+)

Comments

Christoph Hellwig Dec. 6, 2022, 8:45 a.m. UTC | #1
> +static void scrub2_write_endio(struct btrfs_bio *bbio)
> +{
> +	struct scrub2_stripe *stripe = bbio->private;
> +	struct btrfs_fs_info *fs_info = stripe->bg->fs_info;
> +	struct bio_vec *first_bvec = bio_first_bvec_all(&bbio->bio);
> +	struct bio_vec *bvec;
> +	struct bvec_iter_all iter_all;
> +	unsigned long flags;
> +	int bio_size = 0;
> +	int first_sector_nr;
> +	int i;
> +
> +	bio_for_each_segment_all(bvec, &bbio->bio, iter_all)
> +		bio_size += bvec->bv_len;

If you use bio_for_each_bvec_all instead the loop will be significantly
cheaper.
diff mbox series

Patch

diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 15c95cf88a2e..a581d1e4ae44 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -152,6 +152,15 @@  struct scrub2_stripe {
 	 */
 	unsigned long meta_error_bitmap;
 
+	/* This is only for write back cases (repair or replace). */
+	unsigned long write_error_bitmap;
+	
+	/*
+	 * Spinlock for write_error_bitmap, as that's the only case we can have
+	 * multiple bios for one stripe.
+	 */
+	spinlock_t write_error_bitmap_lock;
+
 	/*
 	 * Checksum for the whole stripe if this stripe is inside a data block
 	 * group.
@@ -392,6 +401,7 @@  struct scrub2_stripe *alloc_scrub2_stripe(struct btrfs_fs_info *fs_info,
 
 	init_waitqueue_head(&stripe->io_wait);
 	atomic_set(&stripe->pending_io, 0);
+	spin_lock_init(&stripe->write_error_bitmap_lock);
 
 	stripe->nr_sectors = BTRFS_STRIPE_LEN >> fs_info->sectorsize_bits;
 
@@ -3925,10 +3935,102 @@  static void scrub2_repair_from_mirror(struct scrub2_stripe *orig,
 	}
 }
 
+static void scrub2_write_endio(struct btrfs_bio *bbio)
+{
+	struct scrub2_stripe *stripe = bbio->private;
+	struct btrfs_fs_info *fs_info = stripe->bg->fs_info;
+	struct bio_vec *first_bvec = bio_first_bvec_all(&bbio->bio);
+	struct bio_vec *bvec;
+	struct bvec_iter_all iter_all;
+	unsigned long flags;
+	int bio_size = 0;
+	int first_sector_nr;
+	int i;
+
+	bio_for_each_segment_all(bvec, &bbio->bio, iter_all)
+		bio_size += bvec->bv_len;
+
+	for (i = 0; i < BTRFS_STRIPE_LEN >> PAGE_SHIFT; i++) {
+		if (stripe->pages[i] == first_bvec->bv_page)
+			break;
+	}
+	/*
+	 * Since our pages should all be from stripe->pages[], we should find
+	 * the page.
+	 */
+	ASSERT(i < BTRFS_STRIPE_LEN >> PAGE_SHIFT);
+	first_sector_nr = ((i << PAGE_SHIFT) + first_bvec->bv_offset) >>
+			  fs_info->sectorsize_bits;
+	bio_put(&bbio->bio);
+
+	spin_lock_irqsave(&stripe->write_error_bitmap_lock, flags);
+	bitmap_set(&stripe->write_error_bitmap, first_sector_nr,
+		   bio_size >> fs_info->sectorsize_bits);
+	spin_unlock_irqrestore(&stripe->write_error_bitmap_lock, flags);
+	if (atomic_dec_and_test(&stripe->pending_io))
+		wake_up(&stripe->io_wait);
+}
+
+/*
+ * Writeback sectors specified by @write_bitmap.
+ *
+ * Called by scrub repair (writeback repaired sectors) or dev-replace
+ * (writeback the sectors to the replace dst device).
+ */
+void scrub2_writeback_sectors(struct scrub2_stripe *stripe,
+			      unsigned long *write_bitmap)
+{
+	struct btrfs_fs_info *fs_info = stripe->bg->fs_info;
+	struct bio *bio = NULL;
+	int sector_nr;
+
+	ASSERT(atomic_read(&stripe->pending_io) == 0);
+
+	/* Go through each initially corrupted sector. */
+	for_each_set_bit(sector_nr, write_bitmap, stripe->nr_sectors) {
+		const int page_index = (sector_nr << fs_info->sectorsize_bits) >>
+					PAGE_SHIFT;
+		const int pgoff = offset_in_page(sector_nr <<
+						 fs_info->sectorsize_bits);
+		int ret;
+
+		/*
+		 * No bio allocated or we can not merge with previous sector
+		 * (previous sector is not a repaired one).
+		 */
+		if (!bio || sector_nr == 0 ||
+		    !(test_bit(sector_nr, &stripe->init_error_bitmap) &&
+		      !test_bit(sector_nr - 1, &stripe->current_error_bitmap))) {
+			if (bio) {
+				atomic_inc(&stripe->pending_io);
+				btrfs_submit_bio(fs_info, bio,
+						 stripe->mirror_num);
+			}
+			bio = btrfs_bio_alloc(BTRFS_STRIPE_LEN >> PAGE_SHIFT,
+					REQ_OP_WRITE, scrub2_write_endio, stripe);
+			ASSERT(bio);
+
+			bio->bi_iter.bi_sector = (stripe->logical +
+				(sector_nr << fs_info->sectorsize_bits)) >>
+				SECTOR_SHIFT;
+		}
+		ret = bio_add_page(bio, stripe->pages[page_index],
+				   fs_info->sectorsize, pgoff);
+		ASSERT(ret == fs_info->sectorsize);
+	}
+	if (bio) {
+		atomic_inc(&stripe->pending_io);
+		btrfs_submit_bio(fs_info, bio, stripe->mirror_num);
+	}
+
+	wait_event(stripe->io_wait, atomic_read(&stripe->pending_io) == 0);
+}
+
 void scrub2_repair_one_stripe(struct scrub2_stripe *stripe)
 {
 	struct btrfs_fs_info *fs_info = stripe->bg->fs_info;
 	struct scrub2_stripe *repair;
+	unsigned long writeback_bitmap = 0;
 	int nr_copies;
 	int i;
 
@@ -3964,6 +4066,13 @@  void scrub2_repair_one_stripe(struct scrub2_stripe *stripe)
 			break;
 	}
 	free_scrub2_stripe(repair);
+	/*
+	 * Writeback the sectors which are in the init_error_bitmap, but not
+	 * int the current_error_bitmap.
+	 * Thus writeback = init_error & !current_error.
+	 */
+	bitmap_andnot(&writeback_bitmap, &stripe->init_error_bitmap,
+		      &stripe->current_error_bitmap, stripe->nr_sectors);
 }
 
 /*
diff --git a/fs/btrfs/scrub.h b/fs/btrfs/scrub.h
index 2f1fceace633..a9519214bd41 100644
--- a/fs/btrfs/scrub.h
+++ b/fs/btrfs/scrub.h
@@ -26,5 +26,7 @@  int scrub2_find_fill_first_stripe(struct btrfs_root *extent_root,
 				  u64 logical_start, u64 logical_len,
 				  struct scrub2_stripe *stripe);
 void scrub2_repair_one_stripe(struct scrub2_stripe *stripe);
+void scrub2_writeback_sectors(struct scrub2_stripe *stripe,
+			      unsigned long *write_bitmap);
 
 #endif