Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 538c2bc4 authored by Joe Thornber's avatar Joe Thornber Committed by Greg Kroah-Hartman
Browse files

dm cache: fix problematic dual use of a single migration count variable



commit a59db67656021fa212e9b95a583f13c34eb67cd9 upstream.

Introduce a new variable to count the number of allocated migration
structures.  The existing variable cache->nr_migrations became
overloaded.  It was used to:

 i) track of the number of migrations in flight for the purposes of
    quiescing during suspend.

 ii) to estimate the amount of background IO occuring.

Recent discard changes meant that REQ_DISCARD bios are processed with
a migration.  Discards are not background IO so nr_migrations was not
incremented.  However this could cause quiescing to complete early.

(i) is now handled with a new variable cache->nr_allocated_migrations.
cache->nr_migrations has been renamed cache->nr_io_migrations.
cleanup_migration() is now called free_io_migration(), since it
decrements that variable.

Also, remove the unused cache->next_migration variable that got replaced
with with prealloc_structs a while ago.

Signed-off-by: default avatarJoe Thornber <ejt@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7bf9ed7e
Loading
Loading
Loading
Loading
+50 −39
Original line number Diff line number Diff line
@@ -222,7 +222,13 @@ struct cache {
	struct list_head need_commit_migrations;
	sector_t migration_threshold;
	wait_queue_head_t migration_wait;
	atomic_t nr_migrations;
	atomic_t nr_allocated_migrations;

	/*
	 * The number of in flight migrations that are performing
	 * background io. eg, promotion, writeback.
	 */
	atomic_t nr_io_migrations;

	wait_queue_head_t quiescing_wait;
	atomic_t quiescing;
@@ -258,7 +264,6 @@ struct cache {
	struct dm_deferred_set *all_io_ds;

	mempool_t *migration_pool;
	struct dm_cache_migration *next_migration;

	struct dm_cache_policy *policy;
	unsigned policy_nr_args;
@@ -349,10 +354,31 @@ static void free_prison_cell(struct cache *cache, struct dm_bio_prison_cell *cel
	dm_bio_prison_free_cell(cache->prison, cell);
}

static struct dm_cache_migration *alloc_migration(struct cache *cache)
{
	struct dm_cache_migration *mg;

	mg = mempool_alloc(cache->migration_pool, GFP_NOWAIT);
	if (mg) {
		mg->cache = cache;
		atomic_inc(&mg->cache->nr_allocated_migrations);
	}

	return mg;
}

static void free_migration(struct dm_cache_migration *mg)
{
	if (atomic_dec_and_test(&mg->cache->nr_allocated_migrations))
		wake_up(&mg->cache->migration_wait);

	mempool_free(mg, mg->cache->migration_pool);
}

static int prealloc_data_structs(struct cache *cache, struct prealloc *p)
{
	if (!p->mg) {
		p->mg = mempool_alloc(cache->migration_pool, GFP_NOWAIT);
		p->mg = alloc_migration(cache);
		if (!p->mg)
			return -ENOMEM;
	}
@@ -381,7 +407,7 @@ static void prealloc_free_structs(struct cache *cache, struct prealloc *p)
		free_prison_cell(cache, p->cell1);

	if (p->mg)
		mempool_free(p->mg, cache->migration_pool);
		free_migration(p->mg);
}

static struct dm_cache_migration *prealloc_get_migration(struct prealloc *p)
@@ -817,24 +843,14 @@ static void remap_to_origin_then_cache(struct cache *cache, struct bio *bio,
 * Migration covers moving data from the origin device to the cache, or
 * vice versa.
 *--------------------------------------------------------------*/
static void free_migration(struct dm_cache_migration *mg)
{
	mempool_free(mg, mg->cache->migration_pool);
}

static void inc_nr_migrations(struct cache *cache)
static void inc_io_migrations(struct cache *cache)
{
	atomic_inc(&cache->nr_migrations);
	atomic_inc(&cache->nr_io_migrations);
}

static void dec_nr_migrations(struct cache *cache)
static void dec_io_migrations(struct cache *cache)
{
	atomic_dec(&cache->nr_migrations);

	/*
	 * Wake the worker in case we're suspending the target.
	 */
	wake_up(&cache->migration_wait);
	atomic_dec(&cache->nr_io_migrations);
}

static void __cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell,
@@ -857,11 +873,10 @@ static void cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell,
	wake_worker(cache);
}

static void cleanup_migration(struct dm_cache_migration *mg)
static void free_io_migration(struct dm_cache_migration *mg)
{
	struct cache *cache = mg->cache;
	dec_io_migrations(mg->cache);
	free_migration(mg);
	dec_nr_migrations(cache);
}

static void migration_failure(struct dm_cache_migration *mg)
@@ -886,7 +901,7 @@ static void migration_failure(struct dm_cache_migration *mg)
		cell_defer(cache, mg->new_ocell, true);
	}

	cleanup_migration(mg);
	free_io_migration(mg);
}

static void migration_success_pre_commit(struct dm_cache_migration *mg)
@@ -897,7 +912,7 @@ static void migration_success_pre_commit(struct dm_cache_migration *mg)
	if (mg->writeback) {
		clear_dirty(cache, mg->old_oblock, mg->cblock);
		cell_defer(cache, mg->old_ocell, false);
		cleanup_migration(mg);
		free_io_migration(mg);
		return;

	} else if (mg->demote) {
@@ -907,14 +922,14 @@ static void migration_success_pre_commit(struct dm_cache_migration *mg)
					     mg->old_oblock);
			if (mg->promote)
				cell_defer(cache, mg->new_ocell, true);
			cleanup_migration(mg);
			free_io_migration(mg);
			return;
		}
	} else {
		if (dm_cache_insert_mapping(cache->cmd, mg->cblock, mg->new_oblock)) {
			DMWARN_LIMIT("promotion failed; couldn't update on disk metadata");
			policy_remove_mapping(cache->policy, mg->new_oblock);
			cleanup_migration(mg);
			free_io_migration(mg);
			return;
		}
	}
@@ -947,7 +962,7 @@ static void migration_success_post_commit(struct dm_cache_migration *mg)
		} else {
			if (mg->invalidate)
				policy_remove_mapping(cache->policy, mg->old_oblock);
			cleanup_migration(mg);
			free_io_migration(mg);
		}

	} else {
@@ -962,7 +977,7 @@ static void migration_success_post_commit(struct dm_cache_migration *mg)
			bio_endio(mg->new_ocell->holder, 0);
			cell_defer(cache, mg->new_ocell, false);
		}
		cleanup_migration(mg);
		free_io_migration(mg);
	}
}

@@ -1178,7 +1193,7 @@ static void promote(struct cache *cache, struct prealloc *structs,
	mg->new_ocell = cell;
	mg->start_jiffies = jiffies;

	inc_nr_migrations(cache);
	inc_io_migrations(cache);
	quiesce_migration(mg);
}

@@ -1201,7 +1216,7 @@ static void writeback(struct cache *cache, struct prealloc *structs,
	mg->new_ocell = NULL;
	mg->start_jiffies = jiffies;

	inc_nr_migrations(cache);
	inc_io_migrations(cache);
	quiesce_migration(mg);
}

@@ -1227,7 +1242,7 @@ static void demote_then_promote(struct cache *cache, struct prealloc *structs,
	mg->new_ocell = new_ocell;
	mg->start_jiffies = jiffies;

	inc_nr_migrations(cache);
	inc_io_migrations(cache);
	quiesce_migration(mg);
}

@@ -1254,7 +1269,7 @@ static void invalidate(struct cache *cache, struct prealloc *structs,
	mg->new_ocell = NULL;
	mg->start_jiffies = jiffies;

	inc_nr_migrations(cache);
	inc_io_migrations(cache);
	quiesce_migration(mg);
}

@@ -1320,7 +1335,7 @@ static void process_discard_bio(struct cache *cache, struct bio *bio)

static bool spare_migration_bandwidth(struct cache *cache)
{
	sector_t current_volume = (atomic_read(&cache->nr_migrations) + 1) *
	sector_t current_volume = (atomic_read(&cache->nr_io_migrations) + 1) *
		cache->sectors_per_block;
	return current_volume < cache->migration_threshold;
}
@@ -1670,7 +1685,7 @@ static void stop_quiescing(struct cache *cache)

static void wait_for_migrations(struct cache *cache)
{
	wait_event(cache->migration_wait, !atomic_read(&cache->nr_migrations));
	wait_event(cache->migration_wait, !atomic_read(&cache->nr_allocated_migrations));
}

static void stop_worker(struct cache *cache)
@@ -1782,9 +1797,6 @@ static void destroy(struct cache *cache)
{
	unsigned i;

	if (cache->next_migration)
		mempool_free(cache->next_migration, cache->migration_pool);

	if (cache->migration_pool)
		mempool_destroy(cache->migration_pool);

@@ -2292,7 +2304,8 @@ static int cache_create(struct cache_args *ca, struct cache **result)
	INIT_LIST_HEAD(&cache->quiesced_migrations);
	INIT_LIST_HEAD(&cache->completed_migrations);
	INIT_LIST_HEAD(&cache->need_commit_migrations);
	atomic_set(&cache->nr_migrations, 0);
	atomic_set(&cache->nr_allocated_migrations, 0);
	atomic_set(&cache->nr_io_migrations, 0);
	init_waitqueue_head(&cache->migration_wait);

	init_waitqueue_head(&cache->quiescing_wait);
@@ -2351,8 +2364,6 @@ static int cache_create(struct cache_args *ca, struct cache **result)
		goto bad;
	}

	cache->next_migration = NULL;

	cache->need_tick_bio = true;
	cache->sized = false;
	cache->invalidate = false;