unsigned policy_version[CACHE_POLICY_VERSION_SIZE];
        size_t policy_hint_size;
        struct dm_cache_statistics stats;
+
+       /*
+        * 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 __save_sm_root(struct dm_cache_metadata *cmd)
+{
+       int r;
+       size_t metadata_len;
+
+       r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
+       if (r < 0)
+               return r;
+
+       return dm_sm_copy_root(cmd->metadata_sm, &cmd->metadata_space_map_root,
+                              metadata_len);
+}
+
+static void __copy_sm_root(struct dm_cache_metadata *cmd,
+                          struct cache_disk_superblock *disk_super)
+{
+       memcpy(&disk_super->metadata_space_map_root,
+              &cmd->metadata_space_map_root,
+              sizeof(cmd->metadata_space_map_root));
+}
+
 static int __write_initial_superblock(struct dm_cache_metadata *cmd)
 {
        int r;
        struct dm_block *sblock;
-       size_t metadata_len;
        struct cache_disk_superblock *disk_super;
        sector_t bdev_size = i_size_read(cmd->bdev->bd_inode) >> SECTOR_SHIFT;
 
        if (bdev_size > DM_CACHE_METADATA_MAX_SECTORS)
                bdev_size = DM_CACHE_METADATA_MAX_SECTORS;
 
-       r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
+       r = dm_tm_pre_commit(cmd->tm);
        if (r < 0)
                return r;
 
-       r = dm_tm_pre_commit(cmd->tm);
-       if (r < 0)
+       /*
+        * dm_sm_copy_root() can fail.  So we need to do it before we start
+        * updating the superblock.
+        */
+       r = __save_sm_root(cmd);
+       if (r)
                return r;
 
        r = superblock_lock_zero(cmd, &sblock);
        memset(disk_super->policy_version, 0, sizeof(disk_super->policy_version));
        disk_super->policy_hint_size = 0;
 
-       r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root,
-                           metadata_len);
-       if (r < 0)
-               goto bad_locked;
+       __copy_sm_root(cmd, disk_super);
 
        disk_super->mapping_root = cpu_to_le64(cmd->root);
        disk_super->hint_root = cpu_to_le64(cmd->hint_root);
        disk_super->write_misses = cpu_to_le32(0);
 
        return dm_tm_commit(cmd->tm, sblock);
-
-bad_locked:
-       dm_bm_unlock(sblock);
-       return r;
 }
 
 static int __format_metadata(struct dm_cache_metadata *cmd)
                                flags_mutator mutator)
 {
        int r;
-       size_t metadata_len;
        struct cache_disk_superblock *disk_super;
        struct dm_block *sblock;
 
        if (r < 0)
                return r;
 
-       r = dm_sm_root_size(cmd->metadata_sm, &metadata_len);
-       if (r < 0)
+       r = __save_sm_root(cmd);
+       if (r)
                return r;
 
        r = superblock_lock(cmd, &sblock);
        disk_super->read_misses = cpu_to_le32(cmd->stats.read_misses);
        disk_super->write_hits = cpu_to_le32(cmd->stats.write_hits);
        disk_super->write_misses = cpu_to_le32(cmd->stats.write_misses);
-
-       r = dm_sm_copy_root(cmd->metadata_sm, &disk_super->metadata_space_map_root,
-                           metadata_len);
-       if (r < 0) {
-               dm_bm_unlock(sblock);
-               return r;
-       }
+       __copy_sm_root(cmd, disk_super);
 
        return dm_tm_commit(cmd->tm, sblock);
 }
 
         * 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,
        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);
 
        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);
        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)
        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);
 }
                }
        }
 
+       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__);
                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);
 }
 
         * operation possible in this state is the closing of the device.
         */
        bool fail_io:1;
+
+       /*
+        * Reading the space map roots can fail, so we read it into these
+        * buffers before the superblock is locked and updated.
+        */
+       __u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
+       __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
 };
 
 struct dm_thin_device {
        pmd->details_info.value_type.equal = NULL;
 }
 
+static int save_sm_roots(struct dm_pool_metadata *pmd)
+{
+       int r;
+       size_t len;
+
+       r = dm_sm_root_size(pmd->metadata_sm, &len);
+       if (r < 0)
+               return r;
+
+       r = dm_sm_copy_root(pmd->metadata_sm, &pmd->metadata_space_map_root, len);
+       if (r < 0)
+               return r;
+
+       r = dm_sm_root_size(pmd->data_sm, &len);
+       if (r < 0)
+               return r;
+
+       return dm_sm_copy_root(pmd->data_sm, &pmd->data_space_map_root, len);
+}
+
+static void copy_sm_roots(struct dm_pool_metadata *pmd,
+                         struct thin_disk_superblock *disk)
+{
+       memcpy(&disk->metadata_space_map_root,
+              &pmd->metadata_space_map_root,
+              sizeof(pmd->metadata_space_map_root));
+
+       memcpy(&disk->data_space_map_root,
+              &pmd->data_space_map_root,
+              sizeof(pmd->data_space_map_root));
+}
+
 static int __write_initial_superblock(struct dm_pool_metadata *pmd)
 {
        int r;
        struct dm_block *sblock;
-       size_t metadata_len, data_len;
        struct thin_disk_superblock *disk_super;
        sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT;
 
        if (bdev_size > THIN_METADATA_MAX_SECTORS)
                bdev_size = THIN_METADATA_MAX_SECTORS;
 
-       r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
-       if (r < 0)
-               return r;
-
-       r = dm_sm_root_size(pmd->data_sm, &data_len);
+       r = dm_sm_commit(pmd->data_sm);
        if (r < 0)
                return r;
 
-       r = dm_sm_commit(pmd->data_sm);
+       r = save_sm_roots(pmd);
        if (r < 0)
                return r;
 
        disk_super->trans_id = 0;
        disk_super->held_root = 0;
 
-       r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
-                           metadata_len);
-       if (r < 0)
-               goto bad_locked;
-
-       r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
-                           data_len);
-       if (r < 0)
-               goto bad_locked;
+       copy_sm_roots(pmd, disk_super);
 
        disk_super->data_mapping_root = cpu_to_le64(pmd->root);
        disk_super->device_details_root = cpu_to_le64(pmd->details_root);
        disk_super->data_block_size = cpu_to_le32(pmd->data_block_size);
 
        return dm_tm_commit(pmd->tm, sblock);
-
-bad_locked:
-       dm_bm_unlock(sblock);
-       return r;
 }
 
 static int __format_metadata(struct dm_pool_metadata *pmd)
        if (r < 0)
                return r;
 
+       r = save_sm_roots(pmd);
+       if (r < 0)
+               return r;
+
        r = superblock_lock(pmd, &sblock);
        if (r)
                return r;
        disk_super->trans_id = cpu_to_le64(pmd->trans_id);
        disk_super->flags = cpu_to_le32(pmd->flags);
 
-       r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
-                           metadata_len);
-       if (r < 0)
-               goto out_locked;
-
-       r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
-                           data_len);
-       if (r < 0)
-               goto out_locked;
+       copy_sm_roots(pmd, disk_super);
 
        return dm_tm_commit(pmd->tm, sblock);
-
-out_locked:
-       dm_bm_unlock(sblock);
-       return r;
 }
 
 struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,