diff options
author | Joe Thornber <ejt@redhat.com> | 2013-05-10 14:37:19 +0100 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2013-05-10 14:37:19 +0100 |
commit | 24347e9595704464f62a4ed8f46abf62b4c79cdd (patch) | |
tree | d522f4239193edda8bb204d2c0b4251f2e982bbb /drivers/md | |
parent | 1921c56d95c4ac92b359ad44ffbc1e9a36060b29 (diff) | |
download | linux-stable-24347e9595704464f62a4ed8f46abf62b4c79cdd.tar.gz linux-stable-24347e9595704464f62a4ed8f46abf62b4c79cdd.tar.bz2 linux-stable-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.c | 12 | ||||
-rw-r--r-- | drivers/md/dm-thin-metadata.h | 1 | ||||
-rw-r--r-- | drivers/md/dm-thin.c | 54 |
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, |