diff options
author | Mike Snitzer <snitzer@redhat.com> | 2013-08-22 15:56:18 +0200 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2013-09-06 02:46:06 +0200 |
commit | 94563badaf41f9291ff0bad94a443a4319b9e312 (patch) | |
tree | 6c9bf7f6aba642dd4a08035fe93ec683c3fdf5bf /drivers/md/dm-thin.c | |
parent | dm ioctl: cleanup error handling in table_load (diff) | |
download | linux-94563badaf41f9291ff0bad94a443a4319b9e312.tar.xz linux-94563badaf41f9291ff0bad94a443a4319b9e312.zip |
dm thin: always return -ENOSPC if no_free_space is set
If pool has 'no_free_space' set it means a previous allocation already
determined the pool has no free space (and failed that allocation with
-ENOSPC). By always returning -ENOSPC if 'no_free_space' is set, we do
not allow the pool to oscillate between allocating blocks and then not.
But a side-effect of this determinism is that if a user wants to be able
to allocate new blocks they'll need to reload the pool's table (to clear
the 'no_free_space' flag). This reload will happen automatically if the
pool's data volume is resized. But if the user takes action to free a
lot of space by deleting snapshot volumes, etc the pool will no longer
allow data allocations to continue without an intervening table reload.
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Acked-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm-thin.c')
-rw-r--r-- | drivers/md/dm-thin.c | 56 |
1 files changed, 31 insertions, 25 deletions
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 3aff1c27e5b4..ed063427d676 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -918,6 +918,13 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) unsigned long flags; struct pool *pool = tc->pool; + /* + * Once no_free_space is set we must not allow allocation to succeed. + * Otherwise it is difficult to explain, debug, test and support. + */ + if (pool->no_free_space) + return -ENOSPC; + r = dm_pool_get_free_block_count(pool->pmd, &free_blocks); if (r) return r; @@ -932,31 +939,30 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) } if (!free_blocks) { - if (pool->no_free_space) - return -ENOSPC; - else { - /* - * Try to commit to see if that will free up some - * more space. - */ - (void) commit_or_fallback(pool); + /* + * Try to commit to see if that will free up some + * more space. + */ + (void) commit_or_fallback(pool); - r = dm_pool_get_free_block_count(pool->pmd, &free_blocks); - if (r) - return r; + r = dm_pool_get_free_block_count(pool->pmd, &free_blocks); + if (r) + return r; - /* - * If we still have no space we set a flag to avoid - * doing all this checking and return -ENOSPC. - */ - if (!free_blocks) { - DMWARN("%s: no free 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); - return -ENOSPC; - } + /* + * 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 (!free_blocks) { + DMWARN("%s: no free 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); + return -ENOSPC; } } @@ -2695,7 +2701,7 @@ static struct target_type pool_target = { .name = "thin-pool", .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE | DM_TARGET_IMMUTABLE, - .version = {1, 8, 0}, + .version = {1, 9, 0}, .module = THIS_MODULE, .ctr = pool_ctr, .dtr = pool_dtr, @@ -2982,7 +2988,7 @@ static int thin_iterate_devices(struct dm_target *ti, static struct target_type thin_target = { .name = "thin", - .version = {1, 8, 0}, + .version = {1, 9, 0}, .module = THIS_MODULE, .ctr = thin_ctr, .dtr = thin_dtr, |