Skip to content

Commit 1fdf7b8

Browse files
zdhaysgregkh
authored andcommitted
mmc: block: handle complete_work on separate workqueue
commit dcf6e2e 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: 8119697 ("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>
1 parent cb38411 commit 1fdf7b8

File tree

2 files changed

+10
-1
lines changed

2 files changed

+10
-1
lines changed

drivers/mmc/core/block.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2114,7 +2114,7 @@ static void mmc_blk_mq_req_done(struct mmc_request *mrq)
21142114
if (waiting)
21152115
wake_up(&mq->wait);
21162116
else
2117-
kblockd_schedule_work(&mq->complete_work);
2117+
queue_work(mq->card->complete_wq, &mq->complete_work);
21182118

21192119
return;
21202120
}
@@ -2928,6 +2928,13 @@ static int mmc_blk_probe(struct mmc_card *card)
29282928

29292929
mmc_fixup_device(card, mmc_blk_fixups);
29302930

2931+
card->complete_wq = alloc_workqueue("mmc_complete",
2932+
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
2933+
if (unlikely(!card->complete_wq)) {
2934+
pr_err("Failed to create mmc completion workqueue");
2935+
return -ENOMEM;
2936+
}
2937+
29312938
md = mmc_blk_alloc(card);
29322939
if (IS_ERR(md))
29332940
return PTR_ERR(md);
@@ -2991,6 +2998,7 @@ static void mmc_blk_remove(struct mmc_card *card)
29912998
pm_runtime_put_noidle(&card->dev);
29922999
mmc_blk_remove_req(md);
29933000
dev_set_drvdata(&card->dev, NULL);
3001+
destroy_workqueue(card->complete_wq);
29943002
}
29953003

29963004
static int _mmc_blk_suspend(struct mmc_card *card)

include/linux/mmc/card.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ struct mmc_card {
308308
unsigned int nr_parts;
309309

310310
unsigned int bouncesz; /* Bounce buffer size */
311+
struct workqueue_struct *complete_wq; /* Private workqueue */
311312
};
312313

313314
static inline bool mmc_large_sector(struct mmc_card *card)

0 commit comments

Comments
 (0)