diff options
author | Joe Thornber <ejt@redhat.com> | 2015-06-17 08:35:19 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2015-06-17 10:09:23 -0400 |
commit | 6096d91af0b65a3967139b32d5adbb3647858a26 (patch) | |
tree | 1c78bce04092967acee14e57887ab64c48d865a7 | |
parent | b1f11aff04cc86daa0757ada5deb669a92a8f0fb (diff) |
dm space map metadata: fix occasional leak of a metadata block on resize
The metadata space map has a simplified 'bootstrap' mode that is
operational when extending the space maps. Whilst in this mode it's
possible for some refcount decrement operations to become queued (eg, as
a result of shadowing one of the bitmap indexes). These decrements were
not being applied when switching out of bootstrap mode.
The effect of this bug was the leaking of a 4k metadata block. This is
detected by the latest version of thin_check as a non fatal error.
Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org
-rw-r--r-- | drivers/md/persistent-data/dm-space-map-metadata.c | 50 |
1 files changed, 35 insertions, 15 deletions
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index e8a904298887..53091295fce9 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c | |||
@@ -204,6 +204,27 @@ static void in(struct sm_metadata *smm) | |||
204 | smm->recursion_count++; | 204 | smm->recursion_count++; |
205 | } | 205 | } |
206 | 206 | ||
207 | static int apply_bops(struct sm_metadata *smm) | ||
208 | { | ||
209 | int r = 0; | ||
210 | |||
211 | while (!brb_empty(&smm->uncommitted)) { | ||
212 | struct block_op bop; | ||
213 | |||
214 | r = brb_pop(&smm->uncommitted, &bop); | ||
215 | if (r) { | ||
216 | DMERR("bug in bop ring buffer"); | ||
217 | break; | ||
218 | } | ||
219 | |||
220 | r = commit_bop(smm, &bop); | ||
221 | if (r) | ||
222 | break; | ||
223 | } | ||
224 | |||
225 | return r; | ||
226 | } | ||
227 | |||
207 | static int out(struct sm_metadata *smm) | 228 | static int out(struct sm_metadata *smm) |
208 | { | 229 | { |
209 | int r = 0; | 230 | int r = 0; |
@@ -216,21 +237,8 @@ static int out(struct sm_metadata *smm) | |||
216 | return -ENOMEM; | 237 | return -ENOMEM; |
217 | } | 238 | } |
218 | 239 | ||
219 | if (smm->recursion_count == 1) { | 240 | if (smm->recursion_count == 1) |
220 | while (!brb_empty(&smm->uncommitted)) { | 241 | apply_bops(smm); |
221 | struct block_op bop; | ||
222 | |||
223 | r = brb_pop(&smm->uncommitted, &bop); | ||
224 | if (r) { | ||
225 | DMERR("bug in bop ring buffer"); | ||
226 | break; | ||
227 | } | ||
228 | |||
229 | r = commit_bop(smm, &bop); | ||
230 | if (r) | ||
231 | break; | ||
232 | } | ||
233 | } | ||
234 | 242 | ||
235 | smm->recursion_count--; | 243 | smm->recursion_count--; |
236 | 244 | ||
@@ -704,6 +712,12 @@ static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks) | |||
704 | } | 712 | } |
705 | old_len = smm->begin; | 713 | old_len = smm->begin; |
706 | 714 | ||
715 | r = apply_bops(smm); | ||
716 | if (r) { | ||
717 | DMERR("%s: apply_bops failed", __func__); | ||
718 | goto out; | ||
719 | } | ||
720 | |||
707 | r = sm_ll_commit(&smm->ll); | 721 | r = sm_ll_commit(&smm->ll); |
708 | if (r) | 722 | if (r) |
709 | goto out; | 723 | goto out; |
@@ -773,6 +787,12 @@ int dm_sm_metadata_create(struct dm_space_map *sm, | |||
773 | if (r) | 787 | if (r) |
774 | return r; | 788 | return r; |
775 | 789 | ||
790 | r = apply_bops(smm); | ||
791 | if (r) { | ||
792 | DMERR("%s: apply_bops failed", __func__); | ||
793 | return r; | ||
794 | } | ||
795 | |||
776 | return sm_metadata_commit(sm); | 796 | return sm_metadata_commit(sm); |
777 | } | 797 | } |
778 | 798 | ||