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

Commit d14fcf3d authored by Joe Thornber's avatar Joe Thornber Committed by Mike Snitzer
Browse files

dm cache: make sure every metadata function checks fail_io



Otherwise operations may be attempted that will only ever go on to crash
(since the metadata device is either missing or unreliable if 'fail_io'
is set).

Signed-off-by: default avatarJoe Thornber <ejt@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org
parent 3f068040
Loading
Loading
Loading
Loading
+59 −39
Original line number Original line Diff line number Diff line
@@ -868,18 +868,39 @@ static int blocks_are_unmapped_or_clean(struct dm_cache_metadata *cmd,
}
}


#define WRITE_LOCK(cmd)	\
#define WRITE_LOCK(cmd)	\
	if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
	down_write(&cmd->root_lock); \
	if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
		up_write(&cmd->root_lock); \
		return -EINVAL; \
		return -EINVAL; \
	down_write(&cmd->root_lock)
	}


#define WRITE_LOCK_VOID(cmd) \
#define WRITE_LOCK_VOID(cmd) \
	if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
	down_write(&cmd->root_lock); \
	if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
		up_write(&cmd->root_lock); \
		return; \
		return; \
	down_write(&cmd->root_lock)
	}


#define WRITE_UNLOCK(cmd) \
#define WRITE_UNLOCK(cmd) \
	up_write(&cmd->root_lock)
	up_write(&cmd->root_lock)


#define READ_LOCK(cmd) \
	down_read(&cmd->root_lock); \
	if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
		up_read(&cmd->root_lock); \
		return -EINVAL; \
	}

#define READ_LOCK_VOID(cmd)	\
	down_read(&cmd->root_lock); \
	if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
		up_read(&cmd->root_lock); \
		return; \
	}

#define READ_UNLOCK(cmd) \
	up_read(&cmd->root_lock)

int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
{
{
	int r;
	int r;
@@ -1015,22 +1036,20 @@ int dm_cache_load_discards(struct dm_cache_metadata *cmd,
{
{
	int r;
	int r;


	down_read(&cmd->root_lock);
	READ_LOCK(cmd);
	r = __load_discards(cmd, fn, context);
	r = __load_discards(cmd, fn, context);
	up_read(&cmd->root_lock);
	READ_UNLOCK(cmd);


	return r;
	return r;
}
}


dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd)
int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result)
{
{
	dm_cblock_t r;
	READ_LOCK(cmd);
	*result = cmd->cache_blocks;
	READ_UNLOCK(cmd);


	down_read(&cmd->root_lock);
	return 0;
	r = cmd->cache_blocks;
	up_read(&cmd->root_lock);

	return r;
}
}


static int __remove(struct dm_cache_metadata *cmd, dm_cblock_t cblock)
static int __remove(struct dm_cache_metadata *cmd, dm_cblock_t cblock)
@@ -1188,9 +1207,9 @@ int dm_cache_load_mappings(struct dm_cache_metadata *cmd,
{
{
	int r;
	int r;


	down_read(&cmd->root_lock);
	READ_LOCK(cmd);
	r = __load_mappings(cmd, policy, fn, context);
	r = __load_mappings(cmd, policy, fn, context);
	up_read(&cmd->root_lock);
	READ_UNLOCK(cmd);


	return r;
	return r;
}
}
@@ -1215,18 +1234,18 @@ static int __dump_mappings(struct dm_cache_metadata *cmd)


void dm_cache_dump(struct dm_cache_metadata *cmd)
void dm_cache_dump(struct dm_cache_metadata *cmd)
{
{
	down_read(&cmd->root_lock);
	READ_LOCK_VOID(cmd);
	__dump_mappings(cmd);
	__dump_mappings(cmd);
	up_read(&cmd->root_lock);
	READ_UNLOCK(cmd);
}
}


int dm_cache_changed_this_transaction(struct dm_cache_metadata *cmd)
int dm_cache_changed_this_transaction(struct dm_cache_metadata *cmd)
{
{
	int r;
	int r;


	down_read(&cmd->root_lock);
	READ_LOCK(cmd);
	r = cmd->changed;
	r = cmd->changed;
	up_read(&cmd->root_lock);
	READ_UNLOCK(cmd);


	return r;
	return r;
}
}
@@ -1276,9 +1295,9 @@ int dm_cache_set_dirty(struct dm_cache_metadata *cmd,
void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd,
void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd,
				 struct dm_cache_statistics *stats)
				 struct dm_cache_statistics *stats)
{
{
	down_read(&cmd->root_lock);
	READ_LOCK_VOID(cmd);
	*stats = cmd->stats;
	*stats = cmd->stats;
	up_read(&cmd->root_lock);
	READ_UNLOCK(cmd);
}
}


void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
@@ -1312,9 +1331,9 @@ int dm_cache_get_free_metadata_block_count(struct dm_cache_metadata *cmd,
{
{
	int r = -EINVAL;
	int r = -EINVAL;


	down_read(&cmd->root_lock);
	READ_LOCK(cmd);
	r = dm_sm_get_nr_free(cmd->metadata_sm, result);
	r = dm_sm_get_nr_free(cmd->metadata_sm, result);
	up_read(&cmd->root_lock);
	READ_UNLOCK(cmd);


	return r;
	return r;
}
}
@@ -1324,9 +1343,9 @@ int dm_cache_get_metadata_dev_size(struct dm_cache_metadata *cmd,
{
{
	int r = -EINVAL;
	int r = -EINVAL;


	down_read(&cmd->root_lock);
	READ_LOCK(cmd);
	r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
	r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
	up_read(&cmd->root_lock);
	READ_UNLOCK(cmd);


	return r;
	return r;
}
}
@@ -1417,7 +1436,13 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *


int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result)
int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result)
{
{
	return blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result);
	int r;

	READ_LOCK(cmd);
	r = blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result);
	READ_UNLOCK(cmd);

	return r;
}
}


void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd)
void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd)
@@ -1440,10 +1465,7 @@ int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd)
	struct dm_block *sblock;
	struct dm_block *sblock;
	struct cache_disk_superblock *disk_super;
	struct cache_disk_superblock *disk_super;


	/*
	WRITE_LOCK(cmd);
	 * We ignore fail_io for this function.
	 */
	down_write(&cmd->root_lock);
	set_bit(NEEDS_CHECK, &cmd->flags);
	set_bit(NEEDS_CHECK, &cmd->flags);


	r = superblock_lock(cmd, &sblock);
	r = superblock_lock(cmd, &sblock);
@@ -1458,19 +1480,17 @@ int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd)
	dm_bm_unlock(sblock);
	dm_bm_unlock(sblock);


out:
out:
	up_write(&cmd->root_lock);
	WRITE_UNLOCK(cmd);
	return r;
	return r;
}
}


bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd)
int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result)
{
{
	bool needs_check;
	READ_LOCK(cmd);
	*result = !!test_bit(NEEDS_CHECK, &cmd->flags);
	READ_UNLOCK(cmd);


	down_read(&cmd->root_lock);
	return 0;
	needs_check = !!test_bit(NEEDS_CHECK, &cmd->flags);
	up_read(&cmd->root_lock);

	return needs_check;
}
}


int dm_cache_metadata_abort(struct dm_cache_metadata *cmd)
int dm_cache_metadata_abort(struct dm_cache_metadata *cmd)
+2 −2
Original line number Original line Diff line number Diff line
@@ -66,7 +66,7 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd);
 * origin blocks to map to.
 * origin blocks to map to.
 */
 */
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size);
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size);
dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd);
int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result);


int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
				   sector_t discard_block_size,
				   sector_t discard_block_size,
@@ -137,7 +137,7 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *
 */
 */
int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result);
int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result);


bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd);
int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result);
int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd);
int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd);
void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd);
void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd);
void dm_cache_metadata_set_read_write(struct dm_cache_metadata *cmd);
void dm_cache_metadata_set_read_write(struct dm_cache_metadata *cmd);
+10 −2
Original line number Original line Diff line number Diff line
@@ -984,9 +984,14 @@ static void notify_mode_switch(struct cache *cache, enum cache_metadata_mode mod


static void set_cache_mode(struct cache *cache, enum cache_metadata_mode new_mode)
static void set_cache_mode(struct cache *cache, enum cache_metadata_mode new_mode)
{
{
	bool needs_check = dm_cache_metadata_needs_check(cache->cmd);
	bool needs_check;
	enum cache_metadata_mode old_mode = get_cache_mode(cache);
	enum cache_metadata_mode old_mode = get_cache_mode(cache);


	if (dm_cache_metadata_needs_check(cache->cmd, &needs_check)) {
		DMERR("unable to read needs_check flag, setting failure mode");
		new_mode = CM_FAIL;
	}

	if (new_mode == CM_WRITE && needs_check) {
	if (new_mode == CM_WRITE && needs_check) {
		DMERR("%s: unable to switch cache to write mode until repaired.",
		DMERR("%s: unable to switch cache to write mode until repaired.",
		      cache_device_name(cache));
		      cache_device_name(cache));
@@ -3510,6 +3515,7 @@ static void cache_status(struct dm_target *ti, status_type_t type,
	char buf[BDEVNAME_SIZE];
	char buf[BDEVNAME_SIZE];
	struct cache *cache = ti->private;
	struct cache *cache = ti->private;
	dm_cblock_t residency;
	dm_cblock_t residency;
	bool needs_check;


	switch (type) {
	switch (type) {
	case STATUSTYPE_INFO:
	case STATUSTYPE_INFO:
@@ -3583,7 +3589,9 @@ static void cache_status(struct dm_target *ti, status_type_t type,
		else
		else
			DMEMIT("rw ");
			DMEMIT("rw ");


		if (dm_cache_metadata_needs_check(cache->cmd))
		r = dm_cache_metadata_needs_check(cache->cmd, &needs_check);

		if (r || needs_check)
			DMEMIT("needs_check ");
			DMEMIT("needs_check ");
		else
		else
			DMEMIT("- ");
			DMEMIT("- ");