@@ -5362,6 +5362,93 @@ static void scrub_fs_update_veritical(struct scrub_fs_ctx *sfctx,
}
}
+static void scrub_fs_write_endio(struct bio *bio)
+{
+ struct scrub_fs_ctx *sfctx = bio->bi_private;
+ struct bio_vec *bvec;
+ struct bvec_iter_all iter_all;
+ unsigned int bio_size = 0;
+
+ bio_for_each_segment_all(bvec, bio, iter_all)
+ bio_size += bvec->bv_len;
+
+ /* Repair should be inside one stripe. */
+ ASSERT(bio_size <= BTRFS_STRIPE_LEN);
+
+ atomic_dec(&sfctx->bios_under_io);
+ wake_up(&sfctx->wait);
+ bio_put(bio);
+}
+
+static void scrub_fs_repair_mirror(struct scrub_fs_ctx *sfctx,
+ struct btrfs_io_context *bioc, int mirror_nr)
+{
+ struct bio *bio = NULL;
+ int last_sector = -1;
+ int i;
+
+ ASSERT(mirror_nr < bioc->num_stripes);
+
+ for (i = 0; i < sfctx->sectors_per_stripe; i++) {
+ struct scrub_fs_sector *sector =
+ scrub_fs_get_sector(sfctx, i, mirror_nr);
+
+ if (sector->flags & SCRUB_FS_SECTOR_FLAG_DEV_MISSING ||
+ sector->flags & SCRUB_FS_SECTOR_FLAG_GOOD ||
+ !(sector->flags & SCRUB_FS_SECTOR_FLAG_RECOVERABLE))
+ continue;
+
+ /* No bio allocated, alloc a new one. */
+ if (!bio) {
+ blk_opf_t opf = REQ_OP_WRITE | REQ_BACKGROUND;
+
+ if (sector->flags & SCRUB_FS_SECTOR_FLAG_META)
+ opf |= REQ_META;
+
+ bio = bio_alloc(bioc->stripes[mirror_nr].dev->bdev,
+ sfctx->sectors_per_stripe, opf,
+ GFP_KERNEL);
+ /* It's backed up by mempool. */
+ ASSERT(bio);
+
+ bio->bi_iter.bi_sector =
+ (bioc->stripes[mirror_nr].physical +
+ (i << sfctx->fs_info->sectorsize_bits)) >>
+ SECTOR_SHIFT;
+ bio->bi_private = sfctx;
+ bio->bi_end_io = scrub_fs_write_endio;
+
+ last_sector = i - 1;
+ }
+
+ /* Can merge into preivous bio.*/
+ if (last_sector == i - 1) {
+ struct page *page =
+ scrub_fs_get_page(sfctx, i, mirror_nr);
+ unsigned int page_off =
+ scrub_fs_get_page_offset(sfctx, i, mirror_nr);
+ int ret;
+
+ ret = bio_add_page(bio, page, sfctx->fs_info->sectorsize,
+ page_off);
+ ASSERT(ret == sfctx->fs_info->sectorsize);
+ last_sector = i;
+ continue;
+ }
+
+ /* Can not merge, has to submit the current one and retry. */
+ ASSERT(bio);
+ atomic_inc(&sfctx->bios_under_io);
+ submit_bio(bio);
+ bio = NULL;
+ i--;
+ }
+ if (bio) {
+ atomic_inc(&sfctx->bios_under_io);
+ submit_bio(bio);
+ }
+}
+
static int scrub_fs_one_stripe(struct scrub_fs_ctx *sfctx)
{
struct btrfs_fs_info *fs_info = sfctx->fs_info;
@@ -5413,7 +5500,14 @@ static int scrub_fs_one_stripe(struct scrub_fs_ctx *sfctx)
for (i = 0; i < sfctx->sectors_per_stripe; i++)
scrub_fs_update_veritical(sfctx, i);
- /* Place holder for repair write-back code */
+ /* Submit repair*/
+ if (!sfctx->readonly) {
+ for (i = 0; i < sfctx->nr_copies; i++)
+ scrub_fs_repair_mirror(sfctx, bioc, i);
+ wait_event(sfctx->wait, atomic_read(&sfctx->bios_under_io) == 0);
+ }
+ ASSERT(atomic_read(&sfctx->bios_under_io) == 0);
+
out:
btrfs_put_bioc(bioc);
btrfs_bio_counter_dec(fs_info);
This adds the repair functionality for scrub_fs. Since previous patch has implemented the final verification part, all sectors that can be repaired will have SCRUB_FS_SECTOR_FLAG_RECOVERABLE, we just need to submit write bios for them. And just like the old scrub interface, we don't report writeback error. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/scrub.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-)