diff options
author | Zachary Hays <zhays@lexmark.com> | 2019-02-07 10:03:08 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-02-20 10:25:46 +0100 |
commit | c4609e81e0b71b6b1ce3b55be9b00bad4c92dd46 (patch) | |
tree | a6b3e123cffd5d689dcdc2a9b331bebc309bb04c /drivers/mmc | |
parent | 248a2f3cbebe36f1ea2ac32f1e58d2d57f9f9fdd (diff) | |
download | linux-stable-c4609e81e0b71b6b1ce3b55be9b00bad4c92dd46.tar.gz linux-stable-c4609e81e0b71b6b1ce3b55be9b00bad4c92dd46.tar.bz2 linux-stable-c4609e81e0b71b6b1ce3b55be9b00bad4c92dd46.zip |
mmc: block: handle complete_work on separate workqueue
commit dcf6e2e38a1c7ccbc535de5e1d9b14998847499d upstream.
The kblockd workqueue is created with the WQ_MEM_RECLAIM flag set.
This generates a rescuer thread for that queue that will trigger when
the CPU is under heavy load and collect the uncompleted work.
In the case of mmc, this creates the possibility of a deadlock when
there are multiple partitions on the device as other blk-mq work is
also run on the same queue. For example:
- worker 0 claims the mmc host to work on partition 1
- worker 1 attempts to claim the host for partition 2 but has to wait
for worker 0 to finish
- worker 0 schedules complete_work to release the host
- rescuer thread is triggered after time-out and collects the dangling
work
- rescuer thread attempts to complete the work in order starting with
claim host
- the task to release host is now blocked by a task to claim it and
will never be called
The above results in multiple hung tasks that lead to failures to
mount partitions.
Handling complete_work on a separate workqueue avoids this by keeping
the work completion tasks separate from the other blk-mq work. This
allows the host to be released without getting blocked by other tasks
attempting to claim the host.
Signed-off-by: Zachary Hays <zhays@lexmark.com>
Fixes: 81196976ed94 ("mmc: block: Add blk-mq support")
Cc: <stable@vger.kernel.org>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/block.c | 10 |
1 files changed, 9 insertions, 1 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index f6755b86eba2..eee004fb3c3e 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2114,7 +2114,7 @@ static void mmc_blk_mq_req_done(struct mmc_request *mrq) if (waiting) wake_up(&mq->wait); else - kblockd_schedule_work(&mq->complete_work); + queue_work(mq->card->complete_wq, &mq->complete_work); return; } @@ -2928,6 +2928,13 @@ static int mmc_blk_probe(struct mmc_card *card) mmc_fixup_device(card, mmc_blk_fixups); + card->complete_wq = alloc_workqueue("mmc_complete", + WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + if (unlikely(!card->complete_wq)) { + pr_err("Failed to create mmc completion workqueue"); + return -ENOMEM; + } + md = mmc_blk_alloc(card); if (IS_ERR(md)) return PTR_ERR(md); @@ -2991,6 +2998,7 @@ static void mmc_blk_remove(struct mmc_card *card) pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); dev_set_drvdata(&card->dev, NULL); + destroy_workqueue(card->complete_wq); } static int _mmc_blk_suspend(struct mmc_card *card) |