drivers/md/dm-cache-metadata.c | 70 +++++++++++++++++++++++++++++++++++---- drivers/md/dm-cache-metadata.h | 11 ++++++- drivers/md/dm-cache-target.c | 31 +++++++++++++++++ 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 6703023..34df5c0 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -141,6 +141,13 @@ static int superblock_lock(struct dm_cache_metadata *cmd, &sb_validator, sblock); } +static int superblock_read_lock(struct dm_cache_metadata *cmd, + struct dm_block **sblock) +{ + return dm_bm_read_lock(cmd->bm, CACHE_SUPERBLOCK_LOCATION, + &sb_validator, sblock); +} + static int __superblock_all_zeroes(struct dm_block_manager *bm, int *result) { int r; @@ -369,8 +376,7 @@ static int __begin_transaction(struct dm_cache_metadata *cmd) * We re-read the superblock every time. Shouldn't need to do this * really. */ - r = dm_bm_read_lock(cmd->bm, CACHE_SUPERBLOCK_LOCATION, - &sb_validator, &sblock); + r = superblock_read_lock(cmd, &sblock); if (r) return r; @@ -383,7 +389,18 @@ static int __begin_transaction(struct dm_cache_metadata *cmd) return 0; } -static int __commit_transaction(struct dm_cache_metadata *cmd) +static void set_superblock_flags(struct cache_disk_superblock *disk_super, + unsigned flags) +{ + disk_super->flags = cpu_to_le32((uint32_t) flags); +} + +static unsigned get_superblock_flags(struct cache_disk_superblock *disk_super) +{ + return le32_to_cpu(disk_super->flags); +} + +static int __commit_transaction(struct dm_cache_metadata *cmd, unsigned *flags) { int r; size_t metadata_len; @@ -392,7 +409,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd) debug("__commit_transaction\n"); /* - * We need to know if the thin_disk_superblock exceeds a 512-byte sector. + * We need to know if the cache_disk_superblock exceeds a 512-byte sector. */ BUILD_BUG_ON(sizeof(struct cache_disk_superblock) > 512); @@ -411,6 +428,8 @@ static int __commit_transaction(struct dm_cache_metadata *cmd) disk_super = dm_block_data(sblock); debug("root = %lu\n", (unsigned long) cmd->root); disk_super->mapping_root = cpu_to_le64(cmd->root); + if (flags) + set_superblock_flags(disk_super, *flags); r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root, metadata_len); @@ -458,7 +477,11 @@ struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, void dm_cache_metadata_close(struct dm_cache_metadata *cmd) { - __commit_transaction(cmd); + unsigned sb_flags; + + dm_cache_read_superblock_flags(cmd, &sb_flags); + sb_flags &= ~CACHE_DIRTY; + __commit_transaction(cmd, &sb_flags); __destroy_persistent_data_objects(cmd); kfree(cmd); } @@ -503,7 +526,8 @@ static int __insert(struct dm_cache_metadata *cmd, return 0; } -int dm_cache_insert_mapping(struct dm_cache_metadata *cmd, dm_block_t oblock, dm_block_t cblock) +int dm_cache_insert_mapping(struct dm_cache_metadata *cmd, dm_block_t oblock, + dm_block_t cblock) { int r; @@ -568,12 +592,37 @@ int dm_cache_changed_this_transaction(struct dm_cache_metadata *cmd) return r; } -int dm_cache_commit(struct dm_cache_metadata *cmd) +int dm_cache_read_superblock_flags(struct dm_cache_metadata *cmd, unsigned *flags) +{ + struct cache_disk_superblock *disk_super; + struct dm_block *sblock; + int r; + + down_read(&cmd->root_lock); + r = superblock_read_lock(cmd, &sblock); + if (r) { + up_read(&cmd->root_lock); + return r; + } + + disk_super = dm_block_data(sblock); + *flags = get_superblock_flags(disk_super); + + dm_bm_unlock(sblock); + up_read(&cmd->root_lock); + + return 0; +} + +/* + * If @flags is NULL then the superblock's flags are left unchanged. + */ +int dm_cache_commit_with_flags(struct dm_cache_metadata *cmd, unsigned *flags) { int r; down_write(&cmd->root_lock); - r = __commit_transaction(cmd); + r = __commit_transaction(cmd, flags); if (r) goto out; @@ -584,5 +633,10 @@ out: return r; } +int dm_cache_commit(struct dm_cache_metadata *cmd) +{ + return dm_cache_commit_with_flags(cmd, NULL); +} + /*----------------------------------------------------------------*/ diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h index 7c423c8..fd29de5 100644 --- a/drivers/md/dm-cache-metadata.h +++ b/drivers/md/dm-cache-metadata.h @@ -22,6 +22,12 @@ */ #define CACHE_METADATA_MAX_SECTORS (255 * (1 << 14) * (CACHE_METADATA_BLOCK_SIZE / (1 << SECTOR_SHIFT))) +enum superblock_flag_bits { + __CACHE_DIRTY, /* cache policy data is not to be trusted on resume */ +}; + +#define CACHE_DIRTY (1 << __CACHE_DIRTY) + /* * Compat feature flags. Any incompat flags beyond the ones * specified below will prevent use of the thin metadata. @@ -30,7 +36,6 @@ #define CACHE_FEATURE_COMPAT_RO_SUPP 0UL #define CACHE_FEATURE_INCOMPAT_SUPP 0UL - /* * Returns NULL on failure. */ @@ -49,6 +54,10 @@ int dm_cache_load_mappings(struct dm_cache_metadata *cmd, load_mapping_fn fn, void *context); +int dm_cache_read_superblock_flags(struct dm_cache_metadata *cmd, unsigned *flags); + +int dm_cache_commit_with_flags(struct dm_cache_metadata *cmd, unsigned *flags); + int dm_cache_commit(struct dm_cache_metadata *cmd); /*----------------------------------------------------------------*/ diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 1ec209d..d63ad30 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -1267,8 +1267,21 @@ static int cache_end_io(struct dm_target *ti, struct bio *bio, static void cache_postsuspend(struct dm_target *ti) { + int r; + unsigned sb_flags; struct cache_c *c = ti->private; + r = dm_cache_read_superblock_flags(c->cmd, &sb_flags); + if (r) { + DMERR("could not read superblock flags during suspend"); + return; + } + + sb_flags &= ~CACHE_DIRTY; + r = dm_cache_commit_with_flags(c->cmd, &sb_flags); + if (r) + DMERR("could not clear dirty flag in metadata superblock"); + start_quiescing(c); wait_for_migrations(c); stop_worker(c); @@ -1278,9 +1291,27 @@ static void cache_postsuspend(struct dm_target *ti) static void cache_resume(struct dm_target *ti) { + int r; + unsigned sb_flags; struct cache_c *c = ti->private; + c->need_tick_bio = true; do_waker(&c->waker.work); + + r = dm_cache_read_superblock_flags(c->cmd, &sb_flags); + if (r) { + DMERR("could not read superblock flags during resume"); + return; + } + + if (sb_flags & CACHE_DIRTY) + /* FIXME: perform cache policy recovery */ + DMERR("cache metadata was not written cleanly during previous shutdown"); + + sb_flags &= CACHE_DIRTY; + r = dm_cache_commit_with_flags(c->cmd, &sb_flags); + if (r) + DMERR("could not set dirty flag in metadata superblock"); } static int cache_status(struct dm_target *ti, status_type_t type,