Skip to content

Commit

Permalink
dm verity: avoid deadlock
Browse files Browse the repository at this point in the history
A deadlock was found in the prefetch code in the dm verity map
function.  This patch fixes this by transferring the prefetch
to a worker thread and skipping it completely if kmalloc fails.

If generic_make_request is called recursively, it queues the I/O
request on the current->bio_list without making the I/O request
and returns. The routine making the recursive call cannot wait
for the I/O to complete.

The deadlock occurs when one thread grabs the bufio_client
mutex and waits for an I/O to complete but the I/O is queued
on another thread's current->bio_list and is waiting to get
the mutex held by the first thread.

The fix recognises that prefetching is not essential.  If memory
can be allocated, it queues the prefetch request to the worker thread,
but if not, it does nothing.

Signed-off-by: Paul Taysom <taysom@chromium.org>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Cc: stable@kernel.org
  • Loading branch information
Mikulas Patocka authored and Alasdair G Kergon committed Mar 20, 2013
1 parent 58051b9 commit 3b6b781
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
2 changes: 2 additions & 0 deletions drivers/md/dm-bufio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,8 @@ void dm_bufio_prefetch(struct dm_bufio_client *c,
{
struct blk_plug plug;

BUG_ON(dm_bufio_in_request());

blk_start_plug(&plug);
dm_bufio_lock(c);

Expand Down
39 changes: 34 additions & 5 deletions drivers/md/dm-verity.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ struct dm_verity_io {
*/
};

struct dm_verity_prefetch_work {
struct work_struct work;
struct dm_verity *v;
sector_t block;
unsigned n_blocks;
};

static struct shash_desc *io_hash_desc(struct dm_verity *v, struct dm_verity_io *io)
{
return (struct shash_desc *)(io + 1);
Expand Down Expand Up @@ -424,15 +431,18 @@ static void verity_end_io(struct bio *bio, int error)
* The root buffer is not prefetched, it is assumed that it will be cached
* all the time.
*/
static void verity_prefetch_io(struct dm_verity *v, struct dm_verity_io *io)
static void verity_prefetch_io(struct work_struct *work)
{
struct dm_verity_prefetch_work *pw =
container_of(work, struct dm_verity_prefetch_work, work);
struct dm_verity *v = pw->v;
int i;

for (i = v->levels - 2; i >= 0; i--) {
sector_t hash_block_start;
sector_t hash_block_end;
verity_hash_at_level(v, io->block, i, &hash_block_start, NULL);
verity_hash_at_level(v, io->block + io->n_blocks - 1, i, &hash_block_end, NULL);
verity_hash_at_level(v, pw->block, i, &hash_block_start, NULL);
verity_hash_at_level(v, pw->block + pw->n_blocks - 1, i, &hash_block_end, NULL);
if (!i) {
unsigned cluster = ACCESS_ONCE(dm_verity_prefetch_cluster);

Expand All @@ -452,6 +462,25 @@ static void verity_prefetch_io(struct dm_verity *v, struct dm_verity_io *io)
dm_bufio_prefetch(v->bufio, hash_block_start,
hash_block_end - hash_block_start + 1);
}

kfree(pw);
}

static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io)
{
struct dm_verity_prefetch_work *pw;

pw = kmalloc(sizeof(struct dm_verity_prefetch_work),
GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);

if (!pw)
return;

INIT_WORK(&pw->work, verity_prefetch_io);
pw->v = v;
pw->block = io->block;
pw->n_blocks = io->n_blocks;
queue_work(v->verify_wq, &pw->work);
}

/*
Expand Down Expand Up @@ -498,7 +527,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
memcpy(io->io_vec, bio_iovec(bio),
io->io_vec_size * sizeof(struct bio_vec));

verity_prefetch_io(v, io);
verity_submit_prefetch(v, io);

generic_make_request(bio);

Expand Down Expand Up @@ -858,7 +887,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)

static struct target_type verity_target = {
.name = "verity",
.version = {1, 1, 1},
.version = {1, 2, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,
Expand Down

0 comments on commit 3b6b781

Please sign in to comment.