summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorZachary Hays <zhays@lexmark.com>2019-02-07 10:03:08 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-02-20 10:25:46 +0100
commitc4609e81e0b71b6b1ce3b55be9b00bad4c92dd46 (patch)
treea6b3e123cffd5d689dcdc2a9b331bebc309bb04c /drivers/mmc
parent248a2f3cbebe36f1ea2ac32f1e58d2d57f9f9fdd (diff)
downloadlinux-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.c10
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)