forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
task_work_add: generic process-context callbacks
Provide a simple mechanism that allows running code in the (nonatomic) context of the arbitrary task. The caller does task_work_add(task, task_work) and this task executes task_work->func() either from do_notify_resume() or from do_exit(). The callback can rely on PF_EXITING to detect the latter case. "struct task_work" can be embedded in another struct, still it has "void *data" to handle the most common/simple case. This allows us to kill the ->replacement_session_keyring hack, and potentially this can have more users. Performance-wise, this adds 2 "unlikely(!hlist_empty())" checks into tracehook_notify_resume() and do_exit(). But at the same time we can remove the "replacement_session_keyring != NULL" checks from arch/*/signal.c and exit_creds(). Note: task_work_add/task_work_run abuses ->pi_lock. This is only because this lock is already used by lookup_pi_state() to synchronize with do_exit() setting PF_EXITING. Fortunately the scope of this lock in task_work.c is really tiny, and the code is unlikely anyway. Signed-off-by: Oleg Nesterov <[email protected]> Acked-by: David Howells <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Richard Kuo <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Alexander Gordeev <[email protected]> Cc: Chris Zankel <[email protected]> Cc: David Smith <[email protected]> Cc: "Frank Ch. Eigler" <[email protected]> Cc: Geert Uytterhoeven <[email protected]> Cc: Larry Woodman <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Tejun Heo <[email protected]> Cc: Ingo Molnar <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Al Viro <[email protected]>
- Loading branch information
1 parent
62366c8
commit e73f895
Showing
7 changed files
with
136 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#ifndef _LINUX_TASK_WORK_H | ||
#define _LINUX_TASK_WORK_H | ||
|
||
#include <linux/list.h> | ||
#include <linux/sched.h> | ||
|
||
struct task_work; | ||
typedef void (*task_work_func_t)(struct task_work *); | ||
|
||
struct task_work { | ||
struct hlist_node hlist; | ||
task_work_func_t func; | ||
void *data; | ||
}; | ||
|
||
static inline void | ||
init_task_work(struct task_work *twork, task_work_func_t func, void *data) | ||
{ | ||
twork->func = func; | ||
twork->data = data; | ||
} | ||
|
||
int task_work_add(struct task_struct *task, struct task_work *twork, bool); | ||
struct task_work *task_work_cancel(struct task_struct *, task_work_func_t); | ||
void task_work_run(void); | ||
|
||
static inline void exit_task_work(struct task_struct *task) | ||
{ | ||
if (unlikely(!hlist_empty(&task->task_works))) | ||
task_work_run(); | ||
} | ||
|
||
#endif /* _LINUX_TASK_WORK_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
#include <linux/spinlock.h> | ||
#include <linux/task_work.h> | ||
#include <linux/tracehook.h> | ||
|
||
int | ||
task_work_add(struct task_struct *task, struct task_work *twork, bool notify) | ||
{ | ||
unsigned long flags; | ||
int err = -ESRCH; | ||
|
||
#ifndef TIF_NOTIFY_RESUME | ||
if (notify) | ||
return -ENOTSUPP; | ||
#endif | ||
/* | ||
* We must not insert the new work if the task has already passed | ||
* exit_task_work(). We rely on do_exit()->raw_spin_unlock_wait() | ||
* and check PF_EXITING under pi_lock. | ||
*/ | ||
raw_spin_lock_irqsave(&task->pi_lock, flags); | ||
if (likely(!(task->flags & PF_EXITING))) { | ||
hlist_add_head(&twork->hlist, &task->task_works); | ||
err = 0; | ||
} | ||
raw_spin_unlock_irqrestore(&task->pi_lock, flags); | ||
|
||
/* test_and_set_bit() implies mb(), see tracehook_notify_resume(). */ | ||
if (likely(!err) && notify) | ||
set_notify_resume(task); | ||
return err; | ||
} | ||
|
||
struct task_work * | ||
task_work_cancel(struct task_struct *task, task_work_func_t func) | ||
{ | ||
unsigned long flags; | ||
struct task_work *twork; | ||
struct hlist_node *pos; | ||
|
||
raw_spin_lock_irqsave(&task->pi_lock, flags); | ||
hlist_for_each_entry(twork, pos, &task->task_works, hlist) { | ||
if (twork->func == func) { | ||
hlist_del(&twork->hlist); | ||
goto found; | ||
} | ||
} | ||
twork = NULL; | ||
found: | ||
raw_spin_unlock_irqrestore(&task->pi_lock, flags); | ||
|
||
return twork; | ||
} | ||
|
||
void task_work_run(void) | ||
{ | ||
struct task_struct *task = current; | ||
struct hlist_head task_works; | ||
struct hlist_node *pos; | ||
|
||
raw_spin_lock_irq(&task->pi_lock); | ||
hlist_move_list(&task->task_works, &task_works); | ||
raw_spin_unlock_irq(&task->pi_lock); | ||
|
||
if (unlikely(hlist_empty(&task_works))) | ||
return; | ||
/* | ||
* We use hlist to save the space in task_struct, but we want fifo. | ||
* Find the last entry, the list should be short, then process them | ||
* in reverse order. | ||
*/ | ||
for (pos = task_works.first; pos->next; pos = pos->next) | ||
; | ||
|
||
for (;;) { | ||
struct hlist_node **pprev = pos->pprev; | ||
struct task_work *twork = container_of(pos, struct task_work, | ||
hlist); | ||
twork->func(twork); | ||
|
||
if (pprev == &task_works.first) | ||
break; | ||
pos = container_of(pprev, struct hlist_node, next); | ||
} | ||
} |