diff mbox

[5/5] dm thin: unroll issue_discard() to create longer discard bio chains

Message ID 1462463665-85312-6-git-send-email-snitzer@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Mike Snitzer May 5, 2016, 3:54 p.m. UTC
From: Joe Thornber <ejt@redhat.com>

There is little benefit to doing this but it does structure DM thinp's
code to more cleanly use the __blkdev_issue_discard() interface --
particularly in passdown_double_checking_shared_status().

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-thin.c | 104 ++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 69 insertions(+), 35 deletions(-)

Comments

Christoph Hellwig May 6, 2016, 4:12 p.m. UTC | #1
On Thu, May 05, 2016 at 11:54:25AM -0400, Mike Snitzer wrote:
> From: Joe Thornber <ejt@redhat.com>
> 
> There is little benefit to doing this but it does structure DM thinp's
> code to more cleanly use the __blkdev_issue_discard() interface --
> particularly in passdown_double_checking_shared_status().

As-is I think it makes the code a whole lot more convoluted, and especially
the structure that's just used to communicated 4 parameters between
functions which don't even use all of them isn't very helpful.

If we can get rid of begin_discard/end_discard and struct discard_op I
think the rest of the changes would still be useful, though.

> +static int issue_discard(struct discard_op *op, dm_block_t data_b, dm_block_t data_e)
>  {
> +	struct thin_c *tc = op->tc;
>  	sector_t s = block_to_sectors(tc->pool, data_b);
>  	sector_t len = block_to_sectors(tc->pool, data_e - data_b);
> +	return __blkdev_issue_discard(tc->pool_dev->bdev, s, len,
> +				      GFP_NOWAIT, REQ_WRITE | REQ_DISCARD, &op->bio);
> +}

This is a useful helper I think, but passing tc and bio directly would
make it even more obvious.

> +static void end_discard(struct discard_op *op, int r)
> +{
> +	if (op->bio) {
> +		/*
> +		 * Even if one of the calls to issue_discard failed, we
> +		 * need to wait for the chain to complete.
> +		 */
> +		bio_chain(op->bio, op->parent_bio);
> +		submit_bio(REQ_WRITE | REQ_DISCARD, op->bio);

This comment is a bit confusing as there is nothing that appears to wait
on any of the bios involved.  Also if we ever were to get an error
return from issue_discard when op->bio is not set it would do the wrong
thing, although with the current interface that can't actually happen.

> +	blk_finish_plug(&op->plug);
> +
> +	/*
> +	 * Even if r is set, there could be sub discards in flight that we
> +	 * need to wait for.
> +	 */
> +	if (r && !op->parent_bio->bi_error)
> +		op->parent_bio->bi_error = r;

Both the plugging and the bi_error handling would benefit from being
moved into process_prepared_discard_passdown and handled in the common
path.
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joe Thornber May 6, 2016, 4:36 p.m. UTC | #2
On Fri, May 06, 2016 at 06:12:16PM +0200, Christoph Hellwig wrote:
> On Thu, May 05, 2016 at 11:54:25AM -0400, Mike Snitzer wrote:
> > From: Joe Thornber <ejt@redhat.com>
> > 
> > There is little benefit to doing this but it does structure DM thinp's
> > code to more cleanly use the __blkdev_issue_discard() interface --
> > particularly in passdown_double_checking_shared_status().
> 
> As-is I think it makes the code a whole lot more convoluted, and especially
> the structure that's just used to communicated 4 parameters between
> functions which don't even use all of them isn't very helpful.

I started coding it inline, and then factored it out because I thought
it was cleaner.  I'm also likely to lift it into a separate file for
use in dm-cache too.

> This is a useful helper I think, but passing tc and bio directly would
> make it even more obvious.

y, this is exactly what I had.

> Both the plugging and the bi_error handling would benefit from being
> moved into process_prepared_discard_passdown and handled in the common
> path.

But the plugging and error handling are called from two places.
process_prepared_discard_passdown() and
passdown_double_checking_shared_status().

- Joe

(I agree about the confusing comment, we'll remove)
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoph Hellwig May 6, 2016, 4:44 p.m. UTC | #3
On Fri, May 06, 2016 at 05:36:58PM +0100, Joe Thornber wrote:
> But the plugging and error handling are called from two places.
> process_prepared_discard_passdown() and
> passdown_double_checking_shared_status().

passdown_double_checking_shared_status is only called from
process_prepared_discard_passdown.  So the blk_start_plug from
begin_discard and the work in end_discard could be moved into
it trivially.
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joe Thornber May 6, 2016, 4:48 p.m. UTC | #4
On Fri, May 06, 2016 at 06:44:35PM +0200, Christoph Hellwig wrote:
> On Fri, May 06, 2016 at 05:36:58PM +0100, Joe Thornber wrote:
> > But the plugging and error handling are called from two places.
> > process_prepared_discard_passdown() and
> > passdown_double_checking_shared_status().
> 
> passdown_double_checking_shared_status is only called from
> process_prepared_discard_passdown.  So the blk_start_plug from
> begin_discard and the work in end_discard could be moved into
> it trivially.

Good point, will do.  Thx.
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 598a78b..e4bb9da 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -334,26 +334,55 @@  static sector_t block_to_sectors(struct pool *pool, dm_block_t b)
 		(b * pool->sectors_per_block);
 }
 
-static int issue_discard(struct thin_c *tc, dm_block_t data_b, dm_block_t data_e,
-			 struct bio *parent_bio)
+/*----------------------------------------------------------------*/
+
+struct discard_op {
+	struct thin_c *tc;
+	struct blk_plug plug;
+	struct bio *parent_bio;
+	struct bio *bio;
+};
+
+static void begin_discard(struct discard_op *op, struct thin_c *tc, struct bio *parent)
+{
+	BUG_ON(!parent);
+
+	op->tc = tc;
+	blk_start_plug(&op->plug);
+	op->parent_bio = parent;
+	op->bio = NULL;
+}
+
+static int issue_discard(struct discard_op *op, dm_block_t data_b, dm_block_t data_e)
 {
-	int type = REQ_WRITE | REQ_DISCARD;
+	struct thin_c *tc = op->tc;
 	sector_t s = block_to_sectors(tc->pool, data_b);
 	sector_t len = block_to_sectors(tc->pool, data_e - data_b);
-	struct bio *bio = NULL;
-	struct blk_plug plug;
-	int ret;
 
-	blk_start_plug(&plug);
-	ret = __blkdev_issue_discard(tc->pool_dev->bdev, s, len,
-				     GFP_NOWAIT, type, &bio);
-	if (!ret && bio) {
-		bio_chain(bio, parent_bio);
-		submit_bio(type, bio);
+	return __blkdev_issue_discard(tc->pool_dev->bdev, s, len,
+				      GFP_NOWAIT, REQ_WRITE | REQ_DISCARD, &op->bio);
+}
+
+static void end_discard(struct discard_op *op, int r)
+{
+	if (op->bio) {
+		/*
+		 * Even if one of the calls to issue_discard failed, we
+		 * need to wait for the chain to complete.
+		 */
+		bio_chain(op->bio, op->parent_bio);
+		submit_bio(REQ_WRITE | REQ_DISCARD, op->bio);
 	}
-	blk_finish_plug(&plug);
 
-	return ret;
+	blk_finish_plug(&op->plug);
+
+	/*
+	 * Even if r is set, there could be sub discards in flight that we
+	 * need to wait for.
+	 */
+	if (r && !op->parent_bio->bi_error)
+		op->parent_bio->bi_error = r;
+	bio_endio(op->parent_bio);
 }
 
 /*----------------------------------------------------------------*/
@@ -968,24 +997,28 @@  static void process_prepared_discard_no_passdown(struct dm_thin_new_mapping *m)
 	mempool_free(m, tc->pool->mapping_pool);
 }
 
-static int passdown_double_checking_shared_status(struct dm_thin_new_mapping *m)
+/*----------------------------------------------------------------*/
+
+static void passdown_double_checking_shared_status(struct dm_thin_new_mapping *m)
 {
 	/*
 	 * We've already unmapped this range of blocks, but before we
 	 * passdown we have to check that these blocks are now unused.
 	 */
-	int r;
+	int r = 0;
 	bool used = true;
 	struct thin_c *tc = m->tc;
 	struct pool *pool = tc->pool;
 	dm_block_t b = m->data_block, e, end = m->data_block + m->virt_end - m->virt_begin;
+	struct discard_op op;
 
+	begin_discard(&op, tc, m->bio);
 	while (b != end) {
 		/* find start of unmapped run */
 		for (; b < end; b++) {
 			r = dm_pool_block_is_used(pool->pmd, b, &used);
 			if (r)
-				return r;
+				goto out;
 
 			if (!used)
 				break;
@@ -998,20 +1031,20 @@  static int passdown_double_checking_shared_status(struct dm_thin_new_mapping *m)
 		for (e = b + 1; e != end; e++) {
 			r = dm_pool_block_is_used(pool->pmd, e, &used);
 			if (r)
-				return r;
+				goto out;
 
 			if (used)
 				break;
 		}
 
-		r = issue_discard(tc, b, e, m->bio);
+		r = issue_discard(&op, b, e);
 		if (r)
-			return r;
+			goto out;
 
 		b = e;
 	}
-
-	return 0;
+out:
+	end_discard(&op, r);
 }
 
 static void process_prepared_discard_passdown(struct dm_thin_new_mapping *m)
@@ -1021,20 +1054,21 @@  static void process_prepared_discard_passdown(struct dm_thin_new_mapping *m)
 	struct pool *pool = tc->pool;
 
 	r = dm_thin_remove_range(tc->td, m->virt_begin, m->virt_end);
-	if (r)
+	if (r) {
 		metadata_operation_failed(pool, "dm_thin_remove_range", r);
+		bio_io_error(m->bio);
 
-	else if (m->maybe_shared)
-		r = passdown_double_checking_shared_status(m);
-	else
-		r = issue_discard(tc, m->data_block, m->data_block + (m->virt_end - m->virt_begin), m->bio);
+	} else if (m->maybe_shared) {
+		passdown_double_checking_shared_status(m);
+
+	} else {
+		struct discard_op op;
+		begin_discard(&op, tc, m->bio);
+		r = issue_discard(&op, m->data_block,
+				  m->data_block + (m->virt_end - m->virt_begin));
+		end_discard(&op, r);
+	}
 
-	/*
-	 * Even if r is set, there could be sub discards in flight that we
-	 * need to wait for.
-	 */
-	m->bio->bi_error = r;
-	bio_endio(m->bio);
 	cell_defer_no_holder(tc, m->cell);
 	mempool_free(m, pool->mapping_pool);
 }
@@ -1505,11 +1539,11 @@  static void break_up_discard_bio(struct thin_c *tc, dm_block_t begin, dm_block_t
 
 		/*
 		 * The parent bio must not complete before sub discard bios are
-		 * chained to it (see issue_discard's bio_chain)!
+		 * chained to it (see end_discard's bio_chain)!
 		 *
 		 * This per-mapping bi_remaining increment is paired with
 		 * the implicit decrement that occurs via bio_endio() in
-		 * process_prepared_discard_passdown().
+		 * end_discard().
 		 */
 		bio_inc_remaining(bio);
 		if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list))