From 802b09d10bdd2f90e510049b1c52b81edc2ae0a3 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 7 Aug 2020 16:00:53 -0600 Subject: [PATCH 1/2] kernel: split task_work_add() into two separate helpers Some callers may need to make signaling decisions based on the state of the targeted task, and that can only safely be done post adding the task_work to the task. Split task_work_add() into: __task_work_add() - adds the work item __task_work_notify() - sends the notification No functional changes in this patch. Signed-off-by: Jens Axboe --- include/linux/task_work.h | 19 ++++++++++++++++ kernel/task_work.c | 48 +++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/include/linux/task_work.h b/include/linux/task_work.h index 0fb93aafa478..7abbd8df5e13 100644 --- a/include/linux/task_work.h +++ b/include/linux/task_work.h @@ -5,6 +5,8 @@ #include #include +extern struct callback_head work_exited; + typedef void (*task_work_func_t)(struct callback_head *); static inline void @@ -13,6 +15,21 @@ init_task_work(struct callback_head *twork, task_work_func_t func) twork->func = func; } +static inline int __task_work_add(struct task_struct *task, + struct callback_head *work) +{ + struct callback_head *head; + + do { + head = READ_ONCE(task->task_works); + if (unlikely(head == &work_exited)) + return -ESRCH; + work->next = head; + } while (cmpxchg(&task->task_works, head, work) != head); + + return 0; +} + #define TWA_RESUME 1 #define TWA_SIGNAL 2 int task_work_add(struct task_struct *task, struct callback_head *twork, int); @@ -20,6 +37,8 @@ int task_work_add(struct task_struct *task, struct callback_head *twork, int); struct callback_head *task_work_cancel(struct task_struct *, task_work_func_t); void task_work_run(void); +void __task_work_notify(struct task_struct *task, int notify); + static inline void exit_task_work(struct task_struct *task) { task_work_run(); diff --git a/kernel/task_work.c b/kernel/task_work.c index 5c0848ca1287..9bde81481984 100644 --- a/kernel/task_work.c +++ b/kernel/task_work.c @@ -3,7 +3,27 @@ #include #include -static struct callback_head work_exited; /* all we need is ->next == NULL */ +struct callback_head work_exited = { + .next = NULL /* all we need is ->next == NULL */ +}; + +void __task_work_notify(struct task_struct *task, int notify) +{ + unsigned long flags; + + switch (notify) { + case TWA_RESUME: + set_notify_resume(task); + break; + case TWA_SIGNAL: + if (lock_task_sighand(task, &flags)) { + task->jobctl |= JOBCTL_TASK_WORK; + signal_wake_up(task, 0); + unlock_task_sighand(task, &flags); + } + break; + } +} /** * task_work_add - ask the @task to execute @work->func() @@ -27,29 +47,13 @@ static struct callback_head work_exited; /* all we need is ->next == NULL */ int task_work_add(struct task_struct *task, struct callback_head *work, int notify) { - struct callback_head *head; - unsigned long flags; + int ret; - do { - head = READ_ONCE(task->task_works); - if (unlikely(head == &work_exited)) - return -ESRCH; - work->next = head; - } while (cmpxchg(&task->task_works, head, work) != head); - - switch (notify) { - case TWA_RESUME: - set_notify_resume(task); - break; - case TWA_SIGNAL: - if (lock_task_sighand(task, &flags)) { - task->jobctl |= JOBCTL_TASK_WORK; - signal_wake_up(task, 0); - unlock_task_sighand(task, &flags); - } - break; - } + ret = __task_work_add(task, work); + if (unlikely(ret)) + return ret; + __task_work_notify(task, notify); return 0; } -- 2.28.0