Skip to content

Commit

Permalink
dm thin: always return -ENOSPC if no_free_space is set
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
snitm committed Sep 6, 2013
1 parent f11c1c5 commit 94563ba
Showing 1 changed file with 31 additions and 25 deletions.
56 changes: 31 additions & 25 deletions drivers/md/dm-thin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 94563ba

Please sign in to comment.