dm array: fix a reference counting bug in shadow_ablock
[linux-drm-fsl-dcu.git] / drivers / md / persistent-data / dm-array.c
index 172147eb1d4065f55805b0ff6bf77e972e9b28e3..1d75b1dc1e2e2fcdd24a3be8cf9f0168efacd51e 100644 (file)
@@ -317,8 +317,16 @@ static int shadow_ablock(struct dm_array_info *info, dm_block_t *root,
         * The shadow op will often be a noop.  Only insert if it really
         * copied data.
         */
-       if (dm_block_location(*block) != b)
+       if (dm_block_location(*block) != b) {
+               /*
+                * dm_tm_shadow_block will have already decremented the old
+                * block, but it is still referenced by the btree.  We
+                * increment to stop the insert decrementing it below zero
+                * when overwriting the old value.
+                */
+               dm_tm_inc(info->btree_info.tm, b);
                r = insert_ablock(info, index, *block, root);
+       }
 
        return r;
 }
@@ -509,15 +517,18 @@ static int grow_add_tail_block(struct resize *resize)
 static int grow_needs_more_blocks(struct resize *resize)
 {
        int r;
+       unsigned old_nr_blocks = resize->old_nr_full_blocks;
 
        if (resize->old_nr_entries_in_last_block > 0) {
+               old_nr_blocks++;
+
                r = grow_extend_tail_block(resize, resize->max_entries);
                if (r)
                        return r;
        }
 
        r = insert_full_ablocks(resize->info, resize->size_of_block,
-                               resize->old_nr_full_blocks,
+                               old_nr_blocks,
                                resize->new_nr_full_blocks,
                                resize->max_entries, resize->value,
                                &resize->root);