summaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorJoe Thornber <ejt@redhat.com>2013-05-10 15:37:19 +0200
committerAlasdair G Kergon <agk@redhat.com>2013-05-10 15:37:19 +0200
commit24347e9595704464f62a4ed8f46abf62b4c79cdd (patch)
treed522f4239193edda8bb204d2c0b4251f2e982bbb /drivers/md
parentdm persistent data: support space map resizing (diff)
downloadlinux-24347e9595704464f62a4ed8f46abf62b4c79cdd.tar.xz
linux-24347e9595704464f62a4ed8f46abf62b4c79cdd.zip
dm thin: detect metadata device resizing
Allow the dm thin pool metadata device to be extended. Whenever a pool is resumed, detect whether the size of the metadata device has increased, and if so, extend the metadata to use the new space. Signed-off-by: Joe Thornber <ejt@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-thin-metadata.c12
-rw-r--r--drivers/md/dm-thin-metadata.h1
-rw-r--r--drivers/md/dm-thin.c54
3 files changed, 64 insertions, 3 deletions
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 9452a489ed99..f553ed66603c 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -1677,6 +1677,18 @@ int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
return r;
}
+int dm_pool_resize_metadata_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
+{
+ int r = -EINVAL;
+
+ down_write(&pmd->root_lock);
+ if (!pmd->fail_io)
+ r = __resize_space_map(pmd->metadata_sm, new_count);
+ up_write(&pmd->root_lock);
+
+ return r;
+}
+
void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd)
{
down_write(&pmd->root_lock);
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
index 0cecc3702885..ef8dd709e34e 100644
--- a/drivers/md/dm-thin-metadata.h
+++ b/drivers/md/dm-thin-metadata.h
@@ -185,6 +185,7 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result);
* blocks would be lost.
*/
int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
+int dm_pool_resize_metadata_dev(struct dm_pool_metadata *pmd, dm_block_t new_size);
/*
* Flicks the underlying block manager into read only mode, so you know
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index ef021b0c8106..f4632f97bd7b 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -1923,6 +1923,15 @@ static sector_t get_metadata_dev_size(struct block_device *bdev)
return metadata_dev_size;
}
+static dm_block_t get_metadata_dev_size_in_blocks(struct block_device *bdev)
+{
+ sector_t metadata_dev_size = get_metadata_dev_size(bdev);
+
+ sector_div(metadata_dev_size, THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+
+ return metadata_dev_size;
+}
+
/*
* thin-pool <metadata dev> <data dev>
* <data block size (sectors)>
@@ -2132,6 +2141,41 @@ static int maybe_resize_data_dev(struct dm_target *ti, bool *need_commit)
return 0;
}
+static int maybe_resize_metadata_dev(struct dm_target *ti, bool *need_commit)
+{
+ int r;
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ dm_block_t metadata_dev_size, sb_metadata_dev_size;
+
+ *need_commit = false;
+
+ metadata_dev_size = get_metadata_dev_size(pool->md_dev);
+
+ r = dm_pool_get_metadata_dev_size(pool->pmd, &sb_metadata_dev_size);
+ if (r) {
+ DMERR("failed to retrieve data device size");
+ return r;
+ }
+
+ if (metadata_dev_size < sb_metadata_dev_size) {
+ DMERR("metadata device (%llu sectors) too small: expected %llu",
+ metadata_dev_size, sb_metadata_dev_size);
+ return -EINVAL;
+
+ } else if (metadata_dev_size > sb_metadata_dev_size) {
+ r = dm_pool_resize_metadata_dev(pool->pmd, metadata_dev_size);
+ if (r) {
+ DMERR("failed to resize metadata device");
+ return r;
+ }
+
+ *need_commit = true;
+ }
+
+ return 0;
+}
+
/*
* Retrieves the number of blocks of the data device from
* the superblock and compares it to the actual device size,
@@ -2146,7 +2190,7 @@ static int maybe_resize_data_dev(struct dm_target *ti, bool *need_commit)
static int pool_preresume(struct dm_target *ti)
{
int r;
- bool need_commit1;
+ bool need_commit1, need_commit2;
struct pool_c *pt = ti->private;
struct pool *pool = pt->pool;
@@ -2161,7 +2205,11 @@ static int pool_preresume(struct dm_target *ti)
if (r)
return r;
- if (need_commit1)
+ r = maybe_resize_metadata_dev(ti, &need_commit2);
+ if (r)
+ return r;
+
+ if (need_commit1 || need_commit2)
(void) commit_or_fallback(pool);
return 0;
@@ -2583,7 +2631,7 @@ static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 7, 0},
+ .version = {1, 8, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,