Commit f494a9c6 authored by Joe Thornber's avatar Joe Thornber Committed by Mike Snitzer

dm cache: cache shrinking support

Allow a cache to shrink if the blocks being removed from the cache are
not dirty.
Signed-off-by: default avatarJoe Thornber <ejt@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent c9d28d5d
...@@ -667,19 +667,85 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd) ...@@ -667,19 +667,85 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd)
kfree(cmd); kfree(cmd);
} }
/*
* Checks that the given cache block is either unmapped or clean.
*/
static int block_unmapped_or_clean(struct dm_cache_metadata *cmd, dm_cblock_t b,
bool *result)
{
int r;
__le64 value;
dm_oblock_t ob;
unsigned flags;
r = dm_array_get_value(&cmd->info, cmd->root, from_cblock(b), &value);
if (r) {
DMERR("block_unmapped_or_clean failed");
return r;
}
unpack_value(value, &ob, &flags);
*result = !((flags & M_VALID) && (flags & M_DIRTY));
return 0;
}
static int blocks_are_unmapped_or_clean(struct dm_cache_metadata *cmd,
dm_cblock_t begin, dm_cblock_t end,
bool *result)
{
int r;
*result = true;
while (begin != end) {
r = block_unmapped_or_clean(cmd, begin, result);
if (r)
return r;
if (!*result) {
DMERR("cache block %llu is dirty",
(unsigned long long) from_cblock(begin));
return 0;
}
begin = to_cblock(from_cblock(begin) + 1);
}
return 0;
}
int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size) int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
{ {
int r; int r;
bool clean;
__le64 null_mapping = pack_value(0, 0); __le64 null_mapping = pack_value(0, 0);
down_write(&cmd->root_lock); down_write(&cmd->root_lock);
__dm_bless_for_disk(&null_mapping); __dm_bless_for_disk(&null_mapping);
if (from_cblock(new_cache_size) < from_cblock(cmd->cache_blocks)) {
r = blocks_are_unmapped_or_clean(cmd, new_cache_size, cmd->cache_blocks, &clean);
if (r) {
__dm_unbless_for_disk(&null_mapping);
goto out;
}
if (!clean) {
DMERR("unable to shrink cache due to dirty blocks");
r = -EINVAL;
__dm_unbless_for_disk(&null_mapping);
goto out;
}
}
r = dm_array_resize(&cmd->info, cmd->root, from_cblock(cmd->cache_blocks), r = dm_array_resize(&cmd->info, cmd->root, from_cblock(cmd->cache_blocks),
from_cblock(new_cache_size), from_cblock(new_cache_size),
&null_mapping, &cmd->root); &null_mapping, &cmd->root);
if (!r) if (!r)
cmd->cache_blocks = new_cache_size; cmd->cache_blocks = new_cache_size;
cmd->changed = true; cmd->changed = true;
out:
up_write(&cmd->root_lock); up_write(&cmd->root_lock);
return r; return r;
......
...@@ -2502,26 +2502,71 @@ static int load_discard(void *context, sector_t discard_block_size, ...@@ -2502,26 +2502,71 @@ static int load_discard(void *context, sector_t discard_block_size,
return 0; return 0;
} }
static dm_cblock_t get_cache_dev_size(struct cache *cache)
{
sector_t size = get_dev_size(cache->cache_dev);
(void) sector_div(size, cache->sectors_per_block);
return to_cblock(size);
}
static bool can_resize(struct cache *cache, dm_cblock_t new_size)
{
if (from_cblock(new_size) > from_cblock(cache->cache_size))
return true;
/*
* We can't drop a dirty block when shrinking the cache.
*/
while (from_cblock(new_size) < from_cblock(cache->cache_size)) {
new_size = to_cblock(from_cblock(new_size) + 1);
if (is_dirty(cache, new_size)) {
DMERR("unable to shrink cache; cache block %llu is dirty",
(unsigned long long) from_cblock(new_size));
return false;
}
}
return true;
}
static int resize_cache_dev(struct cache *cache, dm_cblock_t new_size)
{
int r;
r = dm_cache_resize(cache->cmd, cache->cache_size);
if (r) {
DMERR("could not resize cache metadata");
return r;
}
cache->cache_size = new_size;
return 0;
}
static int cache_preresume(struct dm_target *ti) static int cache_preresume(struct dm_target *ti)
{ {
int r = 0; int r = 0;
struct cache *cache = ti->private; struct cache *cache = ti->private;
sector_t actual_cache_size = get_dev_size(cache->cache_dev); dm_cblock_t csize = get_cache_dev_size(cache);
(void) sector_div(actual_cache_size, cache->sectors_per_block);
/* /*
* Check to see if the cache has resized. * Check to see if the cache has resized.
*/ */
if (from_cblock(cache->cache_size) != actual_cache_size || !cache->sized) { if (!cache->sized) {
cache->cache_size = to_cblock(actual_cache_size); r = resize_cache_dev(cache, csize);
if (r)
r = dm_cache_resize(cache->cmd, cache->cache_size);
if (r) {
DMERR("could not resize cache metadata");
return r; return r;
}
cache->sized = true; cache->sized = true;
} else if (csize != cache->cache_size) {
if (!can_resize(cache, csize))
return -EINVAL;
r = resize_cache_dev(cache, csize);
if (r)
return r;
} }
if (!cache->loaded_mappings) { if (!cache->loaded_mappings) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment