If task_work has already been run on task exit, we don't always know if it's safe to run again. Most call paths don't really care as they can never hit this condition, but io_uring would like to call task work from the fops->release() handler, which can get invoked off do_exit() where we could have already run exit_task_work(). Cc: Oleg Nesterov <oleg@xxxxxxxxxx> Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx> Signed-off-by: Jens Axboe <axboe@xxxxxxxxx> --- include/linux/task_work.h | 2 ++ kernel/task_work.c | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/linux/task_work.h b/include/linux/task_work.h index 088538590e65..893c916d8677 100644 --- a/include/linux/task_work.h +++ b/include/linux/task_work.h @@ -7,6 +7,8 @@ typedef void (*task_work_func_t)(struct callback_head *); +extern struct callback_head task_work_exited; + static inline void init_task_work(struct callback_head *twork, task_work_func_t func) { diff --git a/kernel/task_work.c b/kernel/task_work.c index 9620333423a3..d6a8b4ab4858 100644 --- a/kernel/task_work.c +++ b/kernel/task_work.c @@ -3,7 +3,7 @@ #include <linux/task_work.h> #include <linux/tracehook.h> -static struct callback_head work_exited; /* all we need is ->next == NULL */ +struct callback_head task_work_exited = { }; /** * task_work_add - ask the @task to execute @work->func() @@ -31,7 +31,7 @@ task_work_add(struct task_struct *task, struct callback_head *work, bool notify) do { head = READ_ONCE(task->task_works); - if (unlikely(head == &work_exited)) + if (unlikely(head == &task_work_exited)) return -ESRCH; work->next = head; } while (cmpxchg(&task->task_works, head, work) != head); @@ -95,14 +95,14 @@ void __task_work_run(void) for (;;) { /* * work->func() can do task_work_add(), do not set - * work_exited unless the list is empty. + * task_work_exited unless the list is empty. */ do { head = NULL; work = READ_ONCE(task->task_works); if (!work) { if (task->flags & PF_EXITING) - head = &work_exited; + head = &task_work_exited; else break; } -- 2.26.0