mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
dm verity: avoid deadlock
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
This commit is contained in:
parent
58051b94e0
commit
3b6b7813b1
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
@ -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);
|
||||
|
||||
@ -452,6 +462,25 @@ no_prefetch_cluster:
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -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);
|
||||
|
||||
@ -858,7 +887,7 @@ bad:
|
||||
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user