Suspend all active bios when the pool is suspended This is a proof-of-concept patch that makes it possible to suspend all active bios when the pool is suspended. Signed-off-by: Mikulas Patocka --- drivers/md/dm-bio-prison.c | 29 +++++++++++++++++++++++++++ drivers/md/dm-bio-prison.h | 3 ++ drivers/md/dm-thin.c | 47 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 75 insertions(+), 4 deletions(-) Index: linux-2.6/drivers/md/dm-thin.c =================================================================== --- linux-2.6.orig/drivers/md/dm-thin.c 2014-11-08 00:02:42.000000000 +0100 +++ linux-2.6/drivers/md/dm-thin.c 2014-11-08 00:02:42.000000000 +0100 @@ -194,6 +194,8 @@ struct pool { struct dm_thin_new_mapping *next_mapping; mempool_t *mapping_pool; + atomic_t suspended; + process_bio_fn process_bio; process_bio_fn process_discard; @@ -1532,6 +1534,9 @@ static void process_thin_deferred_bios(s return; } + if (unlikely(atomic_read(&pool->suspended))) + return; + bio_list_init(&bios); spin_lock_irqsave(&tc->lock, flags); @@ -1930,7 +1935,8 @@ static int thin_bio_map(struct dm_target return DM_MAPIO_SUBMITTED; } - if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) { + if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA) || + unlikely(atomic_read(&tc->pool->suspended))) { thin_defer_bio(tc, bio); return DM_MAPIO_SUBMITTED; } @@ -1943,6 +1949,12 @@ static int thin_bio_map(struct dm_target if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result)) return DM_MAPIO_SUBMITTED; + if (unlikely(atomic_read(&tc->pool->suspended))) { + thin_defer_bio(tc, bio); + cell_defer_no_holder_no_free(tc, &cell1); + return DM_MAPIO_SUBMITTED; + } + r = dm_thin_find_block(td, block, 0, &result); /* @@ -2227,6 +2239,7 @@ static struct pool *pool_create(struct m INIT_LIST_HEAD(&pool->prepared_discards); INIT_LIST_HEAD(&pool->active_thins); pool->low_water_triggered = false; + atomic_set(&pool->suspended, 1); pool->shared_read_ds = dm_deferred_set_create(); if (!pool->shared_read_ds) { @@ -2772,22 +2785,46 @@ static void pool_resume(struct dm_target spin_lock_irqsave(&pool->lock, flags); pool->low_water_triggered = false; spin_unlock_irqrestore(&pool->lock, flags); - requeue_bios(pool); - do_waker(&pool->waker.work); + if (atomic_dec_and_test(&pool->suspended)) { + requeue_bios(pool); + do_waker(&pool->waker.work); + } } -static void pool_postsuspend(struct dm_target *ti) +static void pool_presuspend(struct dm_target *ti) { struct pool_c *pt = ti->private; struct pool *pool = pt->pool; + atomic_inc(&pool->suspended); + + dm_drain_prison(pool->prison); + + dm_drain_deferred_set(pool->shared_read_ds); + dm_drain_deferred_set(pool->all_io_ds); + cancel_delayed_work(&pool->waker); cancel_delayed_work(&pool->no_space_timeout); flush_workqueue(pool->wq); + + /* ??? should this be before or after flush_workqueue ? */ + dm_drain_deferred_set(pool->shared_read_ds); + dm_drain_deferred_set(pool->all_io_ds); + (void) commit(pool); } +static void pool_presuspend_undo(struct dm_target *ti) +{ + pool_resume(ti); +} + +static void pool_postsuspend(struct dm_target *ti) +{ + /* nothing */ +} + static int check_arg_count(unsigned argc, unsigned args_required) { if (argc != args_required) { @@ -3218,6 +3255,8 @@ static struct target_type pool_target = .ctr = pool_ctr, .dtr = pool_dtr, .map = pool_map, + .presuspend = pool_presuspend, + .presuspend_undo = pool_presuspend_undo, .postsuspend = pool_postsuspend, .preresume = pool_preresume, .resume = pool_resume, Index: linux-2.6/drivers/md/dm-bio-prison.c =================================================================== --- linux-2.6.orig/drivers/md/dm-bio-prison.c 2014-11-08 00:02:42.000000000 +0100 +++ linux-2.6/drivers/md/dm-bio-prison.c 2014-11-08 00:09:40.000000000 +0100 @@ -11,6 +11,7 @@ #include #include #include +#include /*----------------------------------------------------------------*/ @@ -241,6 +242,18 @@ void dm_cell_error(struct dm_bio_prison } EXPORT_SYMBOL_GPL(dm_cell_error); +void dm_drain_prison(struct dm_bio_prison *prison) +{ + spin_lock_irq(&prison->lock); + if (!RB_EMPTY_ROOT(&prison->cells)) { + spin_unlock_irq(&prison->lock); + msleep(1); + spin_lock_irq(&prison->lock); + } + spin_unlock_irq(&prison->lock); +} +EXPORT_SYMBOL_GPL(dm_drain_prison); + /*----------------------------------------------------------------*/ #define DEFERRED_SET_SIZE 64 @@ -354,6 +367,22 @@ int dm_deferred_set_add_work(struct dm_d } EXPORT_SYMBOL_GPL(dm_deferred_set_add_work); +void dm_drain_deferred_set(struct dm_deferred_set *ds) +{ + int i; +retry: + spin_lock_irq(&ds->lock); + for (i = 0; i < DEFERRED_SET_SIZE; i++) { + if (ds->entries[i].count) { + spin_unlock_irq(&ds->lock); + msleep(1); + goto retry; + } + } + spin_unlock_irq(&ds->lock); +} +EXPORT_SYMBOL_GPL(dm_drain_deferred_set); + /*----------------------------------------------------------------*/ static int __init dm_bio_prison_init(void) Index: linux-2.6/drivers/md/dm-bio-prison.h =================================================================== --- linux-2.6.orig/drivers/md/dm-bio-prison.h 2014-11-08 00:02:42.000000000 +0100 +++ linux-2.6/drivers/md/dm-bio-prison.h 2014-11-08 00:02:42.000000000 +0100 @@ -87,6 +87,7 @@ void dm_cell_release_no_holder(struct dm struct bio_list *inmates); void dm_cell_error(struct dm_bio_prison *prison, struct dm_bio_prison_cell *cell, int error); +void dm_drain_prison(struct dm_bio_prison *prison); /*----------------------------------------------------------------*/ @@ -107,6 +108,8 @@ struct dm_deferred_entry *dm_deferred_en void dm_deferred_entry_dec(struct dm_deferred_entry *entry, struct list_head *head); int dm_deferred_set_add_work(struct dm_deferred_set *ds, struct list_head *work); +void dm_drain_deferred_set(struct dm_deferred_set *ds); + /*----------------------------------------------------------------*/ #endif