diff options
author | Joe Thornber <ejt@redhat.com> | 2014-03-27 15:13:23 +0100 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2014-03-27 21:56:23 +0100 |
commit | 5a32083d03fb543f63489b2946c4948398579ba0 (patch) | |
tree | 4b14059d093b846876fa1203f31c0e31f23bcfe1 /drivers/md/dm-era-target.c | |
parent | dm transaction manager: fix corruption due to non-atomic transaction commit (diff) | |
download | linux-5a32083d03fb543f63489b2946c4948398579ba0.tar.xz linux-5a32083d03fb543f63489b2946c4948398579ba0.zip |
dm: take care to copy the space map roots before locking the superblock
In theory copying the space map root can fail, but in practice it never
does because we're careful to check what size buffer is needed.
But make certain we're able to copy the space map roots before
locking the superblock.
Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org # drop dm-era and dm-cache changes as needed
Diffstat (limited to 'drivers/md/dm-era-target.c')
-rw-r--r-- | drivers/md/dm-era-target.c | 68 |
1 files changed, 42 insertions, 26 deletions
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 03d9560bfd95..414dad4cb49b 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -289,6 +289,12 @@ struct era_metadata { * A flag that is set whenever a writeset has been archived. */ bool archived_writesets; + + /* + * Reading the space map root can fail, so we read it into this + * buffer before the superblock is locked and updated. + */ + __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; }; static int superblock_read_lock(struct era_metadata *md, @@ -453,16 +459,33 @@ bad: return r; } +static int save_sm_root(struct era_metadata *md) +{ + int r; + size_t metadata_len; + + r = dm_sm_root_size(md->sm, &metadata_len); + if (r < 0) + return r; + + return dm_sm_copy_root(md->sm, &md->metadata_space_map_root, + metadata_len); +} + +static void copy_sm_root(struct era_metadata *md, struct superblock_disk *disk) +{ + memcpy(&disk->metadata_space_map_root, + &md->metadata_space_map_root, + sizeof(md->metadata_space_map_root)); +} + /* * Writes a superblock, including the static fields that don't get updated * with every commit (possible optimisation here). 'md' should be fully * constructed when this is called. */ -static int prepare_superblock(struct era_metadata *md, struct superblock_disk *disk) +static void prepare_superblock(struct era_metadata *md, struct superblock_disk *disk) { - int r; - size_t metadata_len; - disk->magic = cpu_to_le64(SUPERBLOCK_MAGIC); disk->flags = cpu_to_le32(0ul); @@ -470,14 +493,7 @@ static int prepare_superblock(struct era_metadata *md, struct superblock_disk *d memset(disk->uuid, 0, sizeof(disk->uuid)); disk->version = cpu_to_le32(MAX_ERA_VERSION); - r = dm_sm_root_size(md->sm, &metadata_len); - if (r < 0) - return r; - - r = dm_sm_copy_root(md->sm, &disk->metadata_space_map_root, - metadata_len); - if (r < 0) - return r; + copy_sm_root(md, disk); disk->data_block_size = cpu_to_le32(md->block_size); disk->metadata_block_size = cpu_to_le32(DM_ERA_METADATA_BLOCK_SIZE >> SECTOR_SHIFT); @@ -488,8 +504,6 @@ static int prepare_superblock(struct era_metadata *md, struct superblock_disk *d disk->writeset_tree_root = cpu_to_le64(md->writeset_tree_root); disk->era_array_root = cpu_to_le64(md->era_array_root); disk->metadata_snap = cpu_to_le64(md->metadata_snap); - - return 0; } static int write_superblock(struct era_metadata *md) @@ -498,17 +512,18 @@ static int write_superblock(struct era_metadata *md) struct dm_block *sblock; struct superblock_disk *disk; + r = save_sm_root(md); + if (r) { + DMERR("%s: save_sm_root failed", __func__); + return r; + } + r = superblock_lock_zero(md, &sblock); if (r) return r; disk = dm_block_data(sblock); - r = prepare_superblock(md, disk); - if (r) { - DMERR("%s: prepare_superblock failed", __func__); - dm_bm_unlock(sblock); /* FIXME: does this commit? */ - return r; - } + prepare_superblock(md, disk); return dm_tm_commit(md->tm, sblock); } @@ -942,6 +957,12 @@ static int metadata_commit(struct era_metadata *md) } } + r = save_sm_root(md); + if (r) { + DMERR("%s: save_sm_root failed", __func__); + return r; + } + r = dm_tm_pre_commit(md->tm); if (r) { DMERR("%s: pre commit failed", __func__); @@ -954,12 +975,7 @@ static int metadata_commit(struct era_metadata *md) return r; } - r = prepare_superblock(md, dm_block_data(sblock)); - if (r) { - DMERR("%s: prepare_superblock failed", __func__); - dm_bm_unlock(sblock); /* FIXME: does this commit? */ - return r; - } + prepare_superblock(md, dm_block_data(sblock)); return dm_tm_commit(md->tm, sblock); } |