@@ -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);
}
/*
@@ -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
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(+)