summaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-thin.c61
1 files changed, 32 insertions, 29 deletions
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 234696009d7..96ce36a1a76 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -198,6 +198,7 @@ struct pool {
};
static enum pool_mode get_pool_mode(struct pool *pool);
+static void out_of_data_space(struct pool *pool);
static void metadata_operation_failed(struct pool *pool, const char *op, int r);
/*
@@ -922,16 +923,8 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
{
int r;
dm_block_t free_blocks;
- 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;
-
if (get_pool_mode(pool) != PM_WRITE)
return -EINVAL;
@@ -958,31 +951,14 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result)
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 (!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 = true;
- 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));
-
metadata_operation_failed(pool, "dm_pool_alloc_data_block", r);
return r;
}
@@ -1006,7 +982,7 @@ static void retry_on_resume(struct bio *bio)
spin_unlock_irqrestore(&pool->lock, flags);
}
-static void no_space(struct pool *pool, struct dm_bio_prison_cell *cell)
+static void retry_bios_on_resume(struct pool *pool, struct dm_bio_prison_cell *cell)
{
struct bio *bio;
struct bio_list bios;
@@ -1119,7 +1095,7 @@ static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
break;
case -ENOSPC:
- no_space(pool, cell);
+ retry_bios_on_resume(pool, cell);
break;
default:
@@ -1197,7 +1173,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block
break;
case -ENOSPC:
- no_space(pool, cell);
+ retry_bios_on_resume(pool, cell);
break;
default:
@@ -1446,15 +1422,42 @@ static void set_pool_mode(struct pool *pool, enum pool_mode mode)
}
}
+static void set_no_free_space(struct pool *pool)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->no_free_space = true;
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
/*
* 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)
+{
+ DMERR_LIMIT("%s: no free data space available.",
+ dm_device_name(pool->pool_md));
+ set_no_free_space(pool);
+ 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_no_free_space(pool);
+ }
+
set_pool_mode(pool, PM_READ_ONLY);
}