From: liyouhong <[email protected]>

metadata_take_snap() bumps the live superblock refcount and then
dm_tm_shadow_block() allocates a new block for the metadata snapshot.
If the subsequent dm_sm_inc_block() of writeset_tree_root or
era_array_root fails, the function only unlocks the clone and
returns.  The newly allocated shadow block is never returned to the
metadata space map, so each failed take-snap permanently leaks one
metadata block.

Free the clone with dm_sm_dec_block() on those error paths, matching
the final step of metadata_drop_snap().

Fixes: eec40579d848 ("dm: add era target")
Signed-off-by: liyouhong <[email protected]>
---
 drivers/md/dm-era-target.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
index 7fe4d19ade4f..ea499adca4ce 100644
--- a/drivers/md/dm-era-target.c
+++ b/drivers/md/dm-era-target.c
@@ -1034,6 +1034,7 @@ static int metadata_checkpoint(struct era_metadata *md)
 static int metadata_take_snap(struct era_metadata *md)
 {
        int r, inc;
+       dm_block_t location;
        struct dm_block *clone;
 
        if (md->metadata_snap != SUPERBLOCK_LOCATION) {
@@ -1071,7 +1072,9 @@ static int metadata_take_snap(struct era_metadata *md)
        r = dm_sm_inc_block(md->sm, md->writeset_tree_root);
        if (r) {
                DMERR("%s: couldn't inc writeset tree root", __func__);
+               location = dm_block_location(clone);
                dm_tm_unlock(md->tm, clone);
+               dm_sm_dec_block(md->sm, location);
                return r;
        }
 
@@ -1079,7 +1082,9 @@ static int metadata_take_snap(struct era_metadata *md)
        if (r) {
                DMERR("%s: couldn't inc era tree root", __func__);
                dm_sm_dec_block(md->sm, md->writeset_tree_root);
+               location = dm_block_location(clone);
                dm_tm_unlock(md->tm, clone);
+               dm_sm_dec_block(md->sm, location);
                return r;
        }
 
-- 
2.25.1


Reply via email to