From 3e38ce5dc329b8e94f8b8377404be1d54f30ade3 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 4 Dec 2013 19:51:33 -0500 Subject: [PATCH 6/8] dm thin: handle failures more consistently Introduce out_of_data_space() and metadata_operation_failed() wrappers, around set_pool_mode(), to assist with improving how failures are handled. Logging is improved and metadata operation failures trigger read-only mode immediately. Also, eliminate redundant set_pool_mode() calls in the two alloc_data_block() caller's error paths. Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer --- drivers/md/dm-thin.c | 82 +++++++++++++++++++++++++++++++------------------ 1 files changed, 52 insertions(+), 30 deletions(-) diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 45b10ef..2f8d272 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -198,7 +198,8 @@ struct pool { }; static enum pool_mode get_pool_mode(struct pool *pool); -static void set_pool_mode(struct pool *pool, enum pool_mode mode); +static void out_of_data_space(struct pool *pool); +static void metadata_operation_failed(struct pool *pool, const char *op, int r); /* * Target context for a pool. @@ -640,9 +641,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) */ r = dm_thin_insert_block(tc->td, m->virt_block, m->data_block); if (r) { - DMERR_LIMIT("%s: dm_thin_insert_block() failed: error = %d", - dm_device_name(pool->pool_md), r); - set_pool_mode(pool, PM_READ_ONLY); + metadata_operation_failed(pool, "dm_thin_insert_block", r); cell_error(pool, m->cell); goto out; } @@ -895,11 +894,8 @@ static int commit(struct pool *pool) return -EINVAL; r = dm_pool_commit_metadata(pool->pmd); - if (r) { - DMERR_LIMIT("%s: dm_pool_commit_metadata failed: error = %d", - dm_device_name(pool->pool_md), r); - set_pool_mode(pool, PM_READ_ONLY); - } + if (r) + metadata_operation_failed(pool, "dm_pool_commit_metadata", r); return r; } @@ -922,8 +918,10 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) return -ENOSPC; r = dm_pool_get_free_block_count(pool->pmd, &free_blocks); - if (r) + if (r) { + metadata_operation_failed(pool, "dm_pool_get_free_block_count", r); return r; + } if (free_blocks <= pool->low_water_blocks && !pool->low_water_triggered) { DMWARN("%s: reached low water mark for data device: sending event.", @@ -944,35 +942,24 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) return r; r = dm_pool_get_free_block_count(pool->pmd, &free_blocks); - if (r) + if (r) { + metadata_operation_failed(pool, "dm_pool_get_free_block_count", r); return r; + } /* - * If we still have no space we set a flag to avoid - * doing all this checking and return -ENOSPC. This - * flag serves as a latch that disallows allocations from - * this pool until the admin takes action (e.g. resize or - * table reload). + * If we still have no space we set the no_free_space flag + * to avoid doing all this checking and return -ENOSPC. */ if (!free_blocks) { - DMWARN("%s: no free data space available.", - dm_device_name(pool->pool_md)); - spin_lock_irqsave(&pool->lock, flags); - pool->no_free_space = 1; - spin_unlock_irqrestore(&pool->lock, flags); + out_of_data_space(pool); return -ENOSPC; } } r = dm_pool_alloc_data_block(pool->pmd, result); if (r) { - if (r == -ENOSPC && - !dm_pool_get_free_metadata_block_count(pool->pmd, &free_blocks) && - !free_blocks) { - DMWARN("%s: no free metadata space available.", - dm_device_name(pool->pool_md)); - set_pool_mode(pool, PM_READ_ONLY); - } + metadata_operation_failed(pool, "dm_pool_alloc_data_block", r); return r; } @@ -1168,7 +1155,6 @@ static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block, default: DMERR_LIMIT("%s: alloc_data_block() failed: error = %d", __func__, r); - set_pool_mode(pool, PM_READ_ONLY); cell_error(pool, cell); break; } @@ -1247,7 +1233,6 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block default: DMERR_LIMIT("%s: alloc_data_block() failed: error = %d", __func__, r); - set_pool_mode(pool, PM_READ_ONLY); cell_error(pool, cell); break; } @@ -1491,6 +1476,43 @@ static void set_pool_mode(struct pool *pool, enum pool_mode mode) } } +/* + * Rather than calling set_pool_mode directly, use these which describe the + * reason for mode degradation. + */ +static void out_of_data_space(struct pool *pool) +{ + unsigned long flags; + + DMERR_LIMIT("%s: no free data space available.", + dm_device_name(pool->pool_md)); + /* + * Set the no_free_space flag, it serves as a latch that disallows allocations + * from the pool until the admin takes action (e.g. resize and table reload). + */ + spin_lock_irqsave(&pool->lock, flags); + pool->no_free_space = 1; + spin_unlock_irqrestore(&pool->lock, flags); + + set_pool_mode(pool, PM_READ_ONLY); +} + +static void metadata_operation_failed(struct pool *pool, const char *op, int r) +{ + dm_block_t free_blocks; + + DMERR_LIMIT("%s: metadata operation '%s' failed: error = %d", + dm_device_name(pool->pool_md), op, r); + + if (r == -ENOSPC && + !dm_pool_get_free_metadata_block_count(pool->pmd, &free_blocks) && + !free_blocks) + DMERR_LIMIT("%s: no free metadata space available.", + dm_device_name(pool->pool_md)); + + set_pool_mode(pool, PM_READ_ONLY); +} + /*----------------------------------------------------------------*/ /* -- 1.7.1