Commit 2d759a46 authored by Joe Thornber's avatar Joe Thornber Committed by Mike Snitzer

dm thin: remap the bios in a cell immediately

This use of direct submission in process_prepared_mapping() reduces
latency for submitting bios in a cell by avoiding adding those bios to
the deferred list and waiting for the next iteration of the worker.

But this direct submission exposes the potential for a race between
releasing a cell and incrementing deferred set.  Fix this by introducing
dm_cell_visit_release() and refactoring inc_remap_and_issue_cell()
accordingly.
Signed-off-by: default avatarJoe Thornber <ejt@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent a374bb21
...@@ -241,6 +241,20 @@ void dm_cell_error(struct dm_bio_prison *prison, ...@@ -241,6 +241,20 @@ void dm_cell_error(struct dm_bio_prison *prison,
} }
EXPORT_SYMBOL_GPL(dm_cell_error); EXPORT_SYMBOL_GPL(dm_cell_error);
void dm_cell_visit_release(struct dm_bio_prison *prison,
void (*visit_fn)(void *, struct dm_bio_prison_cell *),
void *context,
struct dm_bio_prison_cell *cell)
{
unsigned long flags;
spin_lock_irqsave(&prison->lock, flags);
visit_fn(context, cell);
rb_erase(&cell->node, &prison->cells);
spin_unlock_irqrestore(&prison->lock, flags);
}
EXPORT_SYMBOL_GPL(dm_cell_visit_release);
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
#define DEFERRED_SET_SIZE 64 #define DEFERRED_SET_SIZE 64
......
...@@ -89,6 +89,14 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison, ...@@ -89,6 +89,14 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
void dm_cell_error(struct dm_bio_prison *prison, void dm_cell_error(struct dm_bio_prison *prison,
struct dm_bio_prison_cell *cell, int error); struct dm_bio_prison_cell *cell, int error);
/*
* Visits the cell and then releases. Guarantees no new inmates are
* inserted between the visit and release.
*/
void dm_cell_visit_release(struct dm_bio_prison *prison,
void (*visit_fn)(void *, struct dm_bio_prison_cell *),
void *context, struct dm_bio_prison_cell *cell);
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
/* /*
......
...@@ -343,6 +343,15 @@ static void cell_release(struct pool *pool, ...@@ -343,6 +343,15 @@ static void cell_release(struct pool *pool,
dm_bio_prison_free_cell(pool->prison, cell); dm_bio_prison_free_cell(pool->prison, cell);
} }
static void cell_visit_release(struct pool *pool,
void (*fn)(void *, struct dm_bio_prison_cell *),
void *context,
struct dm_bio_prison_cell *cell)
{
dm_cell_visit_release(pool->prison, fn, context, cell);
dm_bio_prison_free_cell(pool->prison, cell);
}
static void cell_release_no_holder(struct pool *pool, static void cell_release_no_holder(struct pool *pool,
struct dm_bio_prison_cell *cell, struct dm_bio_prison_cell *cell,
struct bio_list *bios) struct bio_list *bios)
...@@ -697,55 +706,75 @@ static void overwrite_endio(struct bio *bio, int err) ...@@ -697,55 +706,75 @@ static void overwrite_endio(struct bio *bio, int err)
*/ */
/* /*
* This sends the bios in the cell back to the deferred_bios list. * This sends the bios in the cell, except the original holder, back
* to the deferred_bios list.
*/ */
static void cell_defer(struct thin_c *tc, struct dm_bio_prison_cell *cell) static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *cell)
{ {
struct pool *pool = tc->pool; struct pool *pool = tc->pool;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&tc->lock, flags); spin_lock_irqsave(&tc->lock, flags);
cell_release(pool, cell, &tc->deferred_bio_list); cell_release_no_holder(pool, cell, &tc->deferred_bio_list);
spin_unlock_irqrestore(&tc->lock, flags); spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool); wake_worker(pool);
} }
/* static void thin_defer_bio(struct thin_c *tc, struct bio *bio);
* Same as cell_defer above, except it omits the original holder of the cell.
*/ struct remap_info {
static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *cell) struct thin_c *tc;
struct bio_list defer_bios;
struct bio_list issue_bios;
};
static void __inc_remap_and_issue_cell(void *context,
struct dm_bio_prison_cell *cell)
{ {
struct pool *pool = tc->pool; struct remap_info *info = context;
unsigned long flags; struct bio *bio;
spin_lock_irqsave(&tc->lock, flags); while ((bio = bio_list_pop(&cell->bios))) {
cell_release_no_holder(pool, cell, &tc->deferred_bio_list); if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA))
spin_unlock_irqrestore(&tc->lock, flags); bio_list_add(&info->defer_bios, bio);
else {
inc_all_io_entry(info->tc->pool, bio);
wake_worker(pool); /*
* We can't issue the bios with the bio prison lock
* held, so we add them to a list to issue on
* return from this function.
*/
bio_list_add(&info->issue_bios, bio);
}
}
} }
static void thin_defer_bio(struct thin_c *tc, struct bio *bio);
static void inc_remap_and_issue_cell(struct thin_c *tc, static void inc_remap_and_issue_cell(struct thin_c *tc,
struct dm_bio_prison_cell *cell, struct dm_bio_prison_cell *cell,
dm_block_t block) dm_block_t block)
{ {
struct bio *bio; struct bio *bio;
struct bio_list bios; struct remap_info info;
bio_list_init(&bios); info.tc = tc;
cell_release_no_holder(tc->pool, cell, &bios); bio_list_init(&info.defer_bios);
bio_list_init(&info.issue_bios);
while ((bio = bio_list_pop(&bios))) { /*
if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) * We have to be careful to inc any bios we're about to issue
* before the cell is released, and avoid a race with new bios
* being added to the cell.
*/
cell_visit_release(tc->pool, __inc_remap_and_issue_cell,
&info, cell);
while ((bio = bio_list_pop(&info.defer_bios)))
thin_defer_bio(tc, bio); thin_defer_bio(tc, bio);
else {
inc_all_io_entry(tc->pool, bio); while ((bio = bio_list_pop(&info.issue_bios)))
remap_and_issue(tc, bio, block); remap_and_issue(info.tc, bio, block);
}
}
} }
static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m) static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m)
...@@ -796,10 +825,13 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) ...@@ -796,10 +825,13 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m)
* the bios in the cell. * the bios in the cell.
*/ */
if (bio) { if (bio) {
cell_defer_no_holder(tc, m->cell); inc_remap_and_issue_cell(tc, m->cell, m->data_block);
bio_endio(bio, 0); bio_endio(bio, 0);
} else } else {
cell_defer(tc, m->cell); inc_all_io_entry(tc->pool, m->cell->holder);
remap_and_issue(tc, m->cell->holder, m->data_block);
inc_remap_and_issue_cell(tc, m->cell, m->data_block);
}
out: out:
list_del(&m->list); list_del(&m->list);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment