dm thin: fix unprotected use of prepared_discards list

Fix two places in commit 104655fd4d ("dm thin: support discards") that
didn't use pool->lock to protect against concurrent changes to the
prepared_discards list.

Without this fix, thin_endio() can race with process_discard(), leading
to concurrent list_add()s that result in the processes locking up with
an error like the following:

WARNING: at lib/list_debug.c:32 __list_add+0x8f/0xa0()
...
list_add corruption. next->prev should be prev (ffff880323b96140), but was ffff8801d2c48440. (next=ffff8801d2c485c0).
...
Pid: 17205, comm: kworker/u:1 Tainted: G        W  O 3.4.0-rc3.snitm+ #1
Call Trace:
 [<ffffffff8103ca1f>] warn_slowpath_common+0x7f/0xc0
 [<ffffffff8103cb16>] warn_slowpath_fmt+0x46/0x50
 [<ffffffffa04f6ce6>] ? bio_detain+0xc6/0x210 [dm_thin_pool]
 [<ffffffff8124ff3f>] __list_add+0x8f/0xa0
 [<ffffffffa04f70d2>] process_discard+0x2a2/0x2d0 [dm_thin_pool]
 [<ffffffffa04f6a78>] ? remap_and_issue+0x38/0x50 [dm_thin_pool]
 [<ffffffffa04f7c3b>] process_deferred_bios+0x7b/0x230 [dm_thin_pool]
 [<ffffffffa04f7df0>] ? process_deferred_bios+0x230/0x230 [dm_thin_pool]
 [<ffffffffa04f7e42>] do_worker+0x52/0x60 [dm_thin_pool]
 [<ffffffff81056fa9>] process_one_work+0x129/0x450
 [<ffffffff81059b9c>] worker_thread+0x17c/0x3c0
 [<ffffffff81059a20>] ? manage_workers+0x120/0x120
 [<ffffffff8105eabe>] kthread+0x9e/0xb0
 [<ffffffff814ceda4>] kernel_thread_helper+0x4/0x10
 [<ffffffff8105ea20>] ? kthread_freezable_should_stop+0x70/0x70
 [<ffffffff814ceda0>] ? gs_change+0x13/0x13
---[ end trace 7e0a523bc5e52692 ]---

Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
This commit is contained in:
Mike Snitzer 2012-05-12 01:43:16 +01:00 committed by Alasdair G Kergon
parent 03aaae7cdc
commit c3a0ce2eab

View File

@ -1180,6 +1180,7 @@ static void no_space(struct cell *cell)
static void process_discard(struct thin_c *tc, struct bio *bio) static void process_discard(struct thin_c *tc, struct bio *bio)
{ {
int r; int r;
unsigned long flags;
struct pool *pool = tc->pool; struct pool *pool = tc->pool;
struct cell *cell, *cell2; struct cell *cell, *cell2;
struct cell_key key, key2; struct cell_key key, key2;
@ -1221,7 +1222,9 @@ static void process_discard(struct thin_c *tc, struct bio *bio)
m->bio = bio; m->bio = bio;
if (!ds_add_work(&pool->all_io_ds, &m->list)) { if (!ds_add_work(&pool->all_io_ds, &m->list)) {
spin_lock_irqsave(&pool->lock, flags);
list_add(&m->list, &pool->prepared_discards); list_add(&m->list, &pool->prepared_discards);
spin_unlock_irqrestore(&pool->lock, flags);
wake_worker(pool); wake_worker(pool);
} }
} else { } else {
@ -2629,8 +2632,10 @@ static int thin_endio(struct dm_target *ti,
if (h->all_io_entry) { if (h->all_io_entry) {
INIT_LIST_HEAD(&work); INIT_LIST_HEAD(&work);
ds_dec(h->all_io_entry, &work); ds_dec(h->all_io_entry, &work);
spin_lock_irqsave(&pool->lock, flags);
list_for_each_entry_safe(m, tmp, &work, list) list_for_each_entry_safe(m, tmp, &work, list)
list_add(&m->list, &pool->prepared_discards); list_add(&m->list, &pool->prepared_discards);
spin_unlock_irqrestore(&pool->lock, flags);
} }
mempool_free(h, pool->endio_hook_pool); mempool_free(h, pool->endio_hook_pool);