Commit 197f6acc authored by Tejun Heo's avatar Tejun Heo

workqueue: Make sure struct worker is accessible for wq_worker_comm()

The worker struct could already be freed when wq_worker_comm() tries
to access it for reporting.  This patch protects PF_WQ_WORKER
modifications with wq_pool_attach_mutex and makes wq_worker_comm()
test the flag before dereferencing worker from kthread_data(), which
ensures that it only dereferences when the worker struct is valid.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Reported-by: default avatarLai Jiangshan <jiangshanlai@gmail.com>
Fixes: 6b59808b ("workqueue: Show the latest workqueue name in /proc/PID/{comm,stat,status}")
parent 6b59808b
...@@ -2213,6 +2213,16 @@ static void process_scheduled_works(struct worker *worker) ...@@ -2213,6 +2213,16 @@ static void process_scheduled_works(struct worker *worker)
} }
} }
static void set_pf_worker(bool val)
{
mutex_lock(&wq_pool_attach_mutex);
if (val)
current->flags |= PF_WQ_WORKER;
else
current->flags &= ~PF_WQ_WORKER;
mutex_unlock(&wq_pool_attach_mutex);
}
/** /**
* worker_thread - the worker thread function * worker_thread - the worker thread function
* @__worker: self * @__worker: self
...@@ -2231,7 +2241,7 @@ static int worker_thread(void *__worker) ...@@ -2231,7 +2241,7 @@ static int worker_thread(void *__worker)
struct worker_pool *pool = worker->pool; struct worker_pool *pool = worker->pool;
/* tell the scheduler that this is a workqueue worker */ /* tell the scheduler that this is a workqueue worker */
worker->task->flags |= PF_WQ_WORKER; set_pf_worker(true);
woke_up: woke_up:
spin_lock_irq(&pool->lock); spin_lock_irq(&pool->lock);
...@@ -2239,7 +2249,7 @@ static int worker_thread(void *__worker) ...@@ -2239,7 +2249,7 @@ static int worker_thread(void *__worker)
if (unlikely(worker->flags & WORKER_DIE)) { if (unlikely(worker->flags & WORKER_DIE)) {
spin_unlock_irq(&pool->lock); spin_unlock_irq(&pool->lock);
WARN_ON_ONCE(!list_empty(&worker->entry)); WARN_ON_ONCE(!list_empty(&worker->entry));
worker->task->flags &= ~PF_WQ_WORKER; set_pf_worker(false);
set_task_comm(worker->task, "kworker/dying"); set_task_comm(worker->task, "kworker/dying");
ida_simple_remove(&pool->worker_ida, worker->id); ida_simple_remove(&pool->worker_ida, worker->id);
...@@ -2342,7 +2352,7 @@ static int rescuer_thread(void *__rescuer) ...@@ -2342,7 +2352,7 @@ static int rescuer_thread(void *__rescuer)
* Mark rescuer as worker too. As WORKER_PREP is never cleared, it * Mark rescuer as worker too. As WORKER_PREP is never cleared, it
* doesn't participate in concurrency management. * doesn't participate in concurrency management.
*/ */
rescuer->task->flags |= PF_WQ_WORKER; set_pf_worker(true);
repeat: repeat:
set_current_state(TASK_IDLE); set_current_state(TASK_IDLE);
...@@ -2434,7 +2444,7 @@ static int rescuer_thread(void *__rescuer) ...@@ -2434,7 +2444,7 @@ static int rescuer_thread(void *__rescuer)
if (should_stop) { if (should_stop) {
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
rescuer->task->flags &= ~PF_WQ_WORKER; set_pf_worker(false);
return 0; return 0;
} }
...@@ -4580,8 +4590,6 @@ void show_workqueue_state(void) ...@@ -4580,8 +4590,6 @@ void show_workqueue_state(void)
/* used to show worker information through /proc/PID/{comm,stat,status} */ /* used to show worker information through /proc/PID/{comm,stat,status} */
void wq_worker_comm(char *buf, size_t size, struct task_struct *task) void wq_worker_comm(char *buf, size_t size, struct task_struct *task)
{ {
struct worker *worker;
struct worker_pool *pool;
int off; int off;
/* always show the actual comm */ /* always show the actual comm */
...@@ -4589,28 +4597,30 @@ void wq_worker_comm(char *buf, size_t size, struct task_struct *task) ...@@ -4589,28 +4597,30 @@ void wq_worker_comm(char *buf, size_t size, struct task_struct *task)
if (off < 0) if (off < 0)
return; return;
/* stabilize worker pool association */ /* stabilize PF_WQ_WORKER and worker pool association */
mutex_lock(&wq_pool_attach_mutex); mutex_lock(&wq_pool_attach_mutex);
worker = kthread_data(task); if (task->flags & PF_WQ_WORKER) {
pool = worker->pool; struct worker *worker = kthread_data(task);
struct worker_pool *pool = worker->pool;
if (pool) { if (pool) {
spin_lock_irq(&pool->lock); spin_lock_irq(&pool->lock);
/* /*
* ->desc tracks information (wq name or set_worker_desc()) * ->desc tracks information (wq name or
* for the latest execution. If current, prepend '+', * set_worker_desc()) for the latest execution. If
* otherwise '-'. * current, prepend '+', otherwise '-'.
*/ */
if (worker->desc[0] != '\0') { if (worker->desc[0] != '\0') {
if (worker->current_work) if (worker->current_work)
scnprintf(buf + off, size - off, "+%s", scnprintf(buf + off, size - off, "+%s",
worker->desc); worker->desc);
else else
scnprintf(buf + off, size - off, "-%s", scnprintf(buf + off, size - off, "-%s",
worker->desc); worker->desc);
}
spin_unlock_irq(&pool->lock);
} }
spin_unlock_irq(&pool->lock);
} }
mutex_unlock(&wq_pool_attach_mutex); mutex_unlock(&wq_pool_attach_mutex);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment