forked from mirrors/linux
		
	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: Joe Thornber <ejt@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com> Cc: stable@vger.kernel.org
This commit is contained in:
		
							parent
							
								
									3f0680402c
								
							
						
					
					
						commit
						d14fcf3dd7
					
				
					 3 changed files with 71 additions and 43 deletions
				
			
		| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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("- ");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue