[PATCH 1/5] workqueue, sched: Notify workqueue of scheduling of RUNNING tasks

From: Tejun Heo
Date: Tue Apr 18 2023 - 16:52:15 EST


When a workqueue kworker goes to sleep, wq_worker_sleeping() is called so
that workqueue can manage concurrency. This patch renames
wq_worker_sleeping() to wq_worker_stopping() can calls it whenever a kworker
is scheduled out whether it's going to sleep or not.

Workqueue will use the schedule-out event of running tasks to automatically
detect CPU hogging work items and exclude them from concurrency management
so that they can't stall other work items.

This patch just moves the task_is_running() test from sched_submit_work() to
wq_worker_stopping(). No behavior change is intended. While at it, remove
the already outdated comment which doesn't cover the io_wq case.

Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
Cc: Lai Jiangshan <jiangshanlai@xxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
---
kernel/sched/core.c | 18 ++++++------------
kernel/workqueue.c | 13 ++++++++-----
kernel/workqueue_internal.h | 2 +-
3 files changed, 15 insertions(+), 18 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 0d18c3969f90..1d83ff00d587 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -6650,22 +6650,16 @@ void __noreturn do_task_dead(void)

static inline void sched_submit_work(struct task_struct *tsk)
{
- unsigned int task_flags;
+ unsigned int task_flags = tsk->flags;
+
+ if (task_flags & PF_WQ_WORKER)
+ wq_worker_stopping(tsk);

if (task_is_running(tsk))
return;

- task_flags = tsk->flags;
- /*
- * If a worker goes to sleep, notify and ask workqueue whether it
- * wants to wake up a task to maintain concurrency.
- */
- if (task_flags & (PF_WQ_WORKER | PF_IO_WORKER)) {
- if (task_flags & PF_WQ_WORKER)
- wq_worker_sleeping(tsk);
- else
- io_wq_worker_sleeping(tsk);
- }
+ if (task_flags & PF_IO_WORKER)
+ io_wq_worker_sleeping(tsk);

/*
* spinlock and rwlock must not flush block requests. This will
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index b8b541caed48..6199fbf10cec 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -886,17 +886,20 @@ void wq_worker_running(struct task_struct *task)
}

/**
- * wq_worker_sleeping - a worker is going to sleep
- * @task: task going to sleep
+ * wq_worker_stopping - a worker is stopping
+ * @task: task stopping
*
- * This function is called from schedule() when a busy worker is
- * going to sleep.
+ * This function is called from schedule() when a busy worker is going off the
+ * CPU.
*/
-void wq_worker_sleeping(struct task_struct *task)
+void wq_worker_stopping(struct task_struct *task)
{
struct worker *worker = kthread_data(task);
struct worker_pool *pool;

+ if (task_is_running(task))
+ return;
+
/*
* Rescuers, which may not have all the fields set up like normal
* workers, also reach here, let's not access anything before
diff --git a/kernel/workqueue_internal.h b/kernel/workqueue_internal.h
index e00b1204a8e9..b3b4b2b41d93 100644
--- a/kernel/workqueue_internal.h
+++ b/kernel/workqueue_internal.h
@@ -75,7 +75,7 @@ static inline struct worker *current_wq_worker(void)
* sched/ and workqueue.c.
*/
void wq_worker_running(struct task_struct *task);
-void wq_worker_sleeping(struct task_struct *task);
+void wq_worker_stopping(struct task_struct *task);
work_func_t wq_worker_last_func(struct task_struct *task);

#endif /* _KERNEL_WORKQUEUE_INTERNAL_H */
--
2.40.0