Commit f1c29379 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by David Sterba
Browse files

btrfs: properly abstract the parity raid bio handling



The parity raid write/recover functionality is currently not very well
abstracted from the bio submission and completion handling in volumes.c:

 - the raid56 code directly completes the original btrfs_bio fed into
   btrfs_submit_bio instead of dispatching back to volumes.c
 - the raid56 code consumes the bioc and bio_counter references taken
   by volumes.c, which also leads to special casing of the calls from
   the scrub code into the raid56 code

To fix this up supply a bi_end_io handler that calls back into the
volumes.c machinery, which then puts the bioc, decrements the bio_counter
and completes the original bio, and updates the scrub code to also
take ownership of the bioc and bio_counter in all cases.

Reviewed-by: default avatarNikolay Borisov <nborisov@suse.com>
Reviewed-by: default avatarAnand Jain <anand.jain@oracle.com>
Tested-by: default avatarNikolay Borisov <nborisov@suse.com>
Tested-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent c3a62baf
Loading
Loading
Loading
Loading
+7 −38
Original line number Diff line number Diff line
@@ -275,7 +275,6 @@ static void merge_rbio(struct btrfs_raid_bio *dest,
	/* Also inherit the bitmaps from @victim. */
	bitmap_or(&dest->dbitmap, &victim->dbitmap, &dest->dbitmap,
		  dest->stripe_nsectors);
	dest->generic_bio_cnt += victim->generic_bio_cnt;
	bio_list_init(&victim->bio_list);
}

@@ -814,8 +813,6 @@ static void rbio_orig_end_io(struct btrfs_raid_bio *rbio, blk_status_t err)
	struct bio *cur = bio_list_get(&rbio->bio_list);
	struct bio *extra;

	if (rbio->generic_bio_cnt)
		btrfs_bio_counter_sub(rbio->bioc->fs_info, rbio->generic_bio_cnt);
	/*
	 * Clear the data bitmap, as the rbio may be cached for later usage.
	 * do this before before unlock_stripe() so there will be no new bio
@@ -946,6 +943,7 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info,
	spin_lock_init(&rbio->bio_list_lock);
	INIT_LIST_HEAD(&rbio->stripe_cache);
	INIT_LIST_HEAD(&rbio->hash_list);
	btrfs_get_bioc(bioc);
	rbio->bioc = bioc;
	rbio->nr_pages = num_pages;
	rbio->nr_sectors = num_sectors;
@@ -1813,15 +1811,12 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc)

	rbio = alloc_rbio(fs_info, bioc);
	if (IS_ERR(rbio)) {
		btrfs_put_bioc(bioc);
		ret = PTR_ERR(rbio);
		goto out_dec_counter;
		goto fail;
	}
	rbio->operation = BTRFS_RBIO_WRITE;
	rbio_add_bio(rbio, bio);

	rbio->generic_bio_cnt = 1;

	/*
	 * don't plug on full rbios, just get them out the door
	 * as quickly as we can
@@ -1829,7 +1824,7 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc)
	if (rbio_is_full(rbio)) {
		ret = full_stripe_write(rbio);
		if (ret)
			goto out_dec_counter;
			goto fail;
		return;
	}

@@ -1844,13 +1839,12 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc)
	} else {
		ret = __raid56_parity_write(rbio);
		if (ret)
			goto out_dec_counter;
			goto fail;
	}

	return;

out_dec_counter:
	btrfs_bio_counter_dec(fs_info);
fail:
	bio->bi_status = errno_to_blk_status(ret);
	bio_endio(bio);
}
@@ -2198,18 +2192,11 @@ static int __raid56_parity_recover(struct btrfs_raid_bio *rbio)
 * of the drive.
 */
void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc,
			   int mirror_num, bool generic_io)
			   int mirror_num)
{
	struct btrfs_fs_info *fs_info = bioc->fs_info;
	struct btrfs_raid_bio *rbio;

	if (generic_io) {
		ASSERT(bioc->mirror_num == mirror_num);
		btrfs_bio(bio)->mirror_num = mirror_num;
	} else {
		btrfs_get_bioc(bioc);
	}

	rbio = alloc_rbio(fs_info, bioc);
	if (IS_ERR(rbio)) {
		bio->bi_status = errno_to_blk_status(PTR_ERR(rbio));
@@ -2225,14 +2212,11 @@ void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc,
"%s could not find the bad stripe in raid56 so that we cannot recover any more (bio has logical %llu len %llu, bioc has map_type %llu)",
			   __func__, bio->bi_iter.bi_sector << 9,
			   (u64)bio->bi_iter.bi_size, bioc->map_type);
		kfree(rbio);
		__free_raid_bio(rbio);
		bio->bi_status = BLK_STS_IOERR;
		goto out_end_bio;
	}

	if (generic_io)
		rbio->generic_bio_cnt = 1;

	/*
	 * Loop retry:
	 * for 'mirror == 2', reconstruct from all other stripes.
@@ -2261,8 +2245,6 @@ void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc,
	return;

out_end_bio:
	btrfs_bio_counter_dec(fs_info);
	btrfs_put_bioc(bioc);
	bio_endio(bio);
}

@@ -2326,13 +2308,6 @@ struct btrfs_raid_bio *raid56_parity_alloc_scrub_rbio(struct bio *bio,
	ASSERT(i < rbio->real_stripes);

	bitmap_copy(&rbio->dbitmap, dbitmap, stripe_nsectors);

	/*
	 * We have already increased bio_counter when getting bioc, record it
	 * so we can free it at rbio_orig_end_io().
	 */
	rbio->generic_bio_cnt = 1;

	return rbio;
}

@@ -2772,12 +2747,6 @@ raid56_alloc_missing_rbio(struct bio *bio, struct btrfs_io_context *bioc)
		return NULL;
	}

	/*
	 * When we get bioc, we have already increased bio_counter, record it
	 * so we can free it at rbio_orig_end_io()
	 */
	rbio->generic_bio_cnt = 1;

	return rbio;
}

+1 −3
Original line number Diff line number Diff line
@@ -89,8 +89,6 @@ struct btrfs_raid_bio {
	 */
	int bio_list_bytes;

	int generic_bio_cnt;

	refcount_t refs;

	atomic_t stripes_pending;
@@ -166,7 +164,7 @@ static inline int nr_data_stripes(const struct map_lookup *map)
struct btrfs_device;

void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc,
			   int mirror_num, bool generic_io);
			   int mirror_num);
void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc);

void raid56_add_scrub_pages(struct btrfs_raid_bio *rbio, struct page *page,
+5 −3
Original line number Diff line number Diff line
@@ -1568,8 +1568,7 @@ static int scrub_submit_raid56_bio_wait(struct btrfs_fs_info *fs_info,
				 SECTOR_SHIFT;
	bio->bi_private = &done;
	bio->bi_end_io = scrub_bio_wait_endio;
	raid56_parity_recover(bio, sector->recover->bioc,
			      sector->sblock->mirror_num, false);
	raid56_parity_recover(bio, sector->recover->bioc, sector->sblock->mirror_num);

	wait_for_completion_io(&done);
	return blk_status_to_errno(bio->bi_status);
@@ -2285,6 +2284,7 @@ static void scrub_missing_raid56_end_io(struct bio *bio)
	struct scrub_block *sblock = bio->bi_private;
	struct btrfs_fs_info *fs_info = sblock->sctx->fs_info;

	btrfs_bio_counter_dec(fs_info);
	if (bio->bi_status)
		sblock->no_io_error_seen = 0;

@@ -2385,6 +2385,7 @@ static void scrub_missing_raid56_pages(struct scrub_block *sblock)
	scrub_block_get(sblock);
	scrub_pending_bio_inc(sctx);
	raid56_submit_missing_rbio(rbio);
	btrfs_put_bioc(bioc);
	return;

rbio_out:
@@ -2924,6 +2925,7 @@ static void scrub_parity_bio_endio_worker(struct work_struct *work)
						    work);
	struct scrub_ctx *sctx = sparity->sctx;

	btrfs_bio_counter_dec(sctx->fs_info);
	scrub_free_parity(sparity);
	scrub_pending_bio_dec(sctx);
}
@@ -2974,6 +2976,7 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity)
					      sparity->scrub_dev,
					      &sparity->dbitmap,
					      sparity->nsectors);
	btrfs_put_bioc(bioc);
	if (!rbio)
		goto rbio_out;

@@ -2985,7 +2988,6 @@ static void scrub_parity_check_and_repair(struct scrub_parity *sparity)
	bio_put(bio);
bioc_out:
	btrfs_bio_counter_dec(fs_info);
	btrfs_put_bioc(bioc);
	bitmap_or(&sparity->ebitmap, &sparity->ebitmap, &sparity->dbitmap,
		  sparity->nsectors);
	spin_lock(&sctx->stat_lock);
+17 −1
Original line number Diff line number Diff line
@@ -6684,6 +6684,20 @@ static void btrfs_end_bio_work(struct work_struct *work)
	bio_endio(&bbio->bio);
}

static void btrfs_raid56_end_io(struct bio *bio)
{
	struct btrfs_io_context *bioc = bio->bi_private;
	struct btrfs_bio *bbio = btrfs_bio(bio);

	btrfs_bio_counter_dec(bioc->fs_info);
	bbio->mirror_num = bioc->mirror_num;
	bio->bi_end_io = bioc->end_io;
	bio->bi_private = bioc->private;
	bio->bi_end_io(bio);

	btrfs_put_bioc(bioc);
}

static void btrfs_end_bio(struct bio *bio)
{
	struct btrfs_io_stripe *stripe = bio->bi_private;
@@ -6817,10 +6831,12 @@ void btrfs_submit_bio(struct btrfs_fs_info *fs_info, struct bio *bio, int mirror

	if ((bioc->map_type & BTRFS_BLOCK_GROUP_RAID56_MASK) &&
	    ((btrfs_op(bio) == BTRFS_MAP_WRITE) || (mirror_num > 1))) {
		bio->bi_private = bioc;
		bio->bi_end_io = btrfs_raid56_end_io;
		if (btrfs_op(bio) == BTRFS_MAP_WRITE)
			raid56_parity_write(bio, bioc);
		else
			raid56_parity_recover(bio, bioc, mirror_num, true);
			raid56_parity_recover(bio, bioc, mirror_num);
		return;
	}