Skip to content

Commit 00a0ea3

Browse files
vallishgvsnitm
authored andcommitted
dm thin: do not queue freed thin mapping for next stage processing
process_prepared_discard_passdown_pt1() should cleanup dm_thin_new_mapping in cases of error. dm_pool_inc_data_range() can fail trying to get a block reference: metadata operation 'dm_pool_inc_data_range' failed: error = -61 When dm_pool_inc_data_range() fails, dm thin aborts current metadata transaction and marks pool as PM_READ_ONLY. Memory for thin mapping is released as well. However, current thin mapping will be queued onto next stage as part of queue_passdown_pt2() or passdown_endio(). This dangling thin mapping memory when processed and accessed in next stage will lead to device mapper crashing. Code flow without fix: -> process_prepared_discard_passdown_pt1(m) -> dm_thin_remove_range() -> discard passdown --> passdown_endio(m) queues m onto next stage -> dm_pool_inc_data_range() fails, frees memory m but does not remove it from next stage queue -> process_prepared_discard_passdown_pt2(m) -> processes freed memory m and crashes One such stack: Call Trace: [<ffffffffa037a46f>] dm_cell_release_no_holder+0x2f/0x70 [dm_bio_prison] [<ffffffffa039b6dc>] cell_defer_no_holder+0x3c/0x80 [dm_thin_pool] [<ffffffffa039b88b>] process_prepared_discard_passdown_pt2+0x4b/0x90 [dm_thin_pool] [<ffffffffa0399611>] process_prepared+0x81/0xa0 [dm_thin_pool] [<ffffffffa039e735>] do_worker+0xc5/0x820 [dm_thin_pool] [<ffffffff8152bf54>] ? __schedule+0x244/0x680 [<ffffffff81087e72>] ? pwq_activate_delayed_work+0x42/0xb0 [<ffffffff81089f53>] process_one_work+0x153/0x3f0 [<ffffffff8108a71b>] worker_thread+0x12b/0x4b0 [<ffffffff8108a5f0>] ? rescuer_thread+0x350/0x350 [<ffffffff8108fd6a>] kthread+0xca/0xe0 [<ffffffff8108fca0>] ? kthread_park+0x60/0x60 [<ffffffff81530b45>] ret_from_fork+0x25/0x30 The fix is to first take the block ref count for discarded block and then do a passdown discard of this block. If block ref count fails, then bail out aborting current metadata transaction, mark pool as PM_READ_ONLY and also free current thin mapping memory (existing error handling code) without queueing this thin mapping onto next stage of processing. If block ref count succeeds, then passdown discard of this block. Discard callback of passdown_endio() will queue this thin mapping onto next stage of processing. Code flow with fix: -> process_prepared_discard_passdown_pt1(m) -> dm_thin_remove_range() -> dm_pool_inc_data_range() --> if fails, free memory m and bail out -> discard passdown --> passdown_endio(m) queues m onto next stage Cc: stable <stable@vger.kernel.org> # v4.9+ Reviewed-by: Eduardo Valentin <eduval@amazon.com> Reviewed-by: Cristian Gafton <gafton@amazon.com> Reviewed-by: Anchal Agarwal <anchalag@amazon.com> Signed-off-by: Vallish Vaidyeshwara <vallish@amazon.com> Reviewed-by: Joe Thornber <ejt@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
1 parent c4d097d commit 00a0ea3

File tree

1 file changed

+13
-13
lines changed

1 file changed

+13
-13
lines changed

drivers/md/dm-thin.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,19 @@ static void process_prepared_discard_passdown_pt1(struct dm_thin_new_mapping *m)
10941094
return;
10951095
}
10961096

1097+
/*
1098+
* Increment the unmapped blocks. This prevents a race between the
1099+
* passdown io and reallocation of freed blocks.
1100+
*/
1101+
r = dm_pool_inc_data_range(pool->pmd, m->data_block, data_end);
1102+
if (r) {
1103+
metadata_operation_failed(pool, "dm_pool_inc_data_range", r);
1104+
bio_io_error(m->bio);
1105+
cell_defer_no_holder(tc, m->cell);
1106+
mempool_free(m, pool->mapping_pool);
1107+
return;
1108+
}
1109+
10971110
discard_parent = bio_alloc(GFP_NOIO, 1);
10981111
if (!discard_parent) {
10991112
DMWARN("%s: unable to allocate top level discard bio for passdown. Skipping passdown.",
@@ -1114,19 +1127,6 @@ static void process_prepared_discard_passdown_pt1(struct dm_thin_new_mapping *m)
11141127
end_discard(&op, r);
11151128
}
11161129
}
1117-
1118-
/*
1119-
* Increment the unmapped blocks. This prevents a race between the
1120-
* passdown io and reallocation of freed blocks.
1121-
*/
1122-
r = dm_pool_inc_data_range(pool->pmd, m->data_block, data_end);
1123-
if (r) {
1124-
metadata_operation_failed(pool, "dm_pool_inc_data_range", r);
1125-
bio_io_error(m->bio);
1126-
cell_defer_no_holder(tc, m->cell);
1127-
mempool_free(m, pool->mapping_pool);
1128-
return;
1129-
}
11301130
}
11311131

11321132
static void process_prepared_discard_passdown_pt2(struct dm_thin_new_mapping *m)

0 commit comments

Comments
 (0)