2020-08-27 06:10:30

by qianli zhao

[permalink] [raw]
Subject: [PATCH v4] workqueue: Warn when work flush own workqueue

From: Qianli Zhao <[email protected]>

If a workqueue flushes itself then that will lead to
a deadlock. Print a warning and a stack trace when
this happens.

crash> ps 10856
PID PPID CPU TASK ST COMM
10856 2 2 ffffffc873428080 UN [kworker/u16:15]
crash> bt 10856
PID: 10856 TASK: ffffffc873428080 CPU: 2 COMMAND: "kworker/u16:15"
#0 [ffffff80270cb9a0] __switch_to at ffffff99bba8533c
#1 [ffffff80270cba30] __schedule at ffffff99bcda18dc
#2 [ffffff80270cba50] schedule at ffffff99bcda1cdc
#3 [ffffff80270cbaf0] schedule_timeout at ffffff99bcda6674
#4 [ffffff80270cbb70] wait_for_common at ffffff99bcda2c68
#5 [ffffff80270cbb80] wait_for_completion at ffffff99bcda2b60
#6 [ffffff80270cbc30] flush_workqueue at ffffff99bbad7a60
#7 [ffffff80270cbc90] drain_workqueue at ffffff99bbad80fc
#8 [ffffff80270cbcb0] destroy_workqueue at ffffff99bbad92f8
#9 [ffffff80270cbda0] dfc_svc_init at ffffff99bbfbfb6c
#10 [ffffff80270cbdf0] process_one_work at ffffff99bbadc478
#11 [ffffff80270cbe50] worker_thread at ffffff99bbadc9dc
#12 [ffffff80270cbeb0] kthread at ffffff99bbae1f84

Reported-by: kernel test robot <[email protected]>
Signed-off-by: Qianli Zhao <[email protected]>
---
Changes in V4:
- Fix an error that when different work item used same workqueue in flush_work, patch will trigger WARNING incorrectly, reported-by "kernel test robot <[email protected]>"

Changes in V3:
- update changelog
- update comment

Changes in V2:
- update changelog
- update comment

kernel/workqueue.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index c41c3c1..6a8a4e0 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -2585,6 +2585,7 @@ static int rescuer_thread(void *__rescuer)
* @target_work: work item being flushed (NULL for workqueue flushes)
*
* %current is trying to flush the whole @target_wq or @target_work on it.
+ * If a work queue flushes itself, that will lead to a deadlock.
* If @target_wq doesn't have %WQ_MEM_RECLAIM, verify that %current is not
* reclaiming memory or running on a workqueue which doesn't have
* %WQ_MEM_RECLAIM as that can break forward-progress guarantee leading to
@@ -2594,13 +2595,17 @@ static void check_flush_dependency(struct workqueue_struct *target_wq,
struct work_struct *target_work)
{
work_func_t target_func = target_work ? target_work->func : NULL;
- struct worker *worker;
+ struct worker *worker = current_wq_worker();
+
+ WARN_ONCE(worker && worker->current_pwq->wq == target_wq &&
+ worker->task == current &&
+ (target_work ? worker->current_work == target_work : true),
+ "workqueue: current work function:%ps is flushing own workqueue:%s",
+ worker->current_func, target_wq->name);

if (target_wq->flags & WQ_MEM_RECLAIM)
return;

- worker = current_wq_worker();
-
WARN_ONCE(current->flags & PF_MEMALLOC,
"workqueue: PF_MEMALLOC task %d(%s) is flushing !WQ_MEM_RECLAIM %s:%ps",
current->pid, current->comm, target_wq->name, target_func);
--
2.7.4


2020-09-04 17:05:40

by Tejun Heo

[permalink] [raw]
Subject: Re: [PATCH v4] workqueue: Warn when work flush own workqueue

On Thu, Aug 27, 2020 at 02:09:11PM +0800, Qianli Zhao wrote:
> @@ -2594,13 +2595,17 @@ static void check_flush_dependency(struct workqueue_struct *target_wq,
> struct work_struct *target_work)
> {
> work_func_t target_func = target_work ? target_work->func : NULL;
> - struct worker *worker;
> + struct worker *worker = current_wq_worker();
> +
> + WARN_ONCE(worker && worker->current_pwq->wq == target_wq &&
> + worker->task == current &&
> + (target_work ? worker->current_work == target_work : true),
> + "workqueue: current work function:%ps is flushing own workqueue:%s",
> + worker->current_func, target_wq->name);

So, the idea is that we catch these with lockdeps. Doesn't lockdep trigger
for the same condition?

Thanks.

--
tejun