Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e73f8959 authored by Oleg Nesterov's avatar Oleg Nesterov Committed by Al Viro
Browse files

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: default avatarOleg Nesterov <oleg@redhat.com>
Acked-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Richard Kuo <rkuo@codeaurora.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Alexander Gordeev <agordeev@redhat.com>
Cc: Chris Zankel <chris@zankel.net>
Cc: David Smith <dsmith@redhat.com>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Larry Woodman <lwoodman@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 62366c88
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -1400,6 +1400,8 @@ struct task_struct {
	int (*notifier)(void *priv);
	int (*notifier)(void *priv);
	void *notifier_data;
	void *notifier_data;
	sigset_t *notifier_mask;
	sigset_t *notifier_mask;
	struct hlist_head task_works;

	struct audit_context *audit_context;
	struct audit_context *audit_context;
#ifdef CONFIG_AUDITSYSCALL
#ifdef CONFIG_AUDITSYSCALL
	uid_t loginuid;
	uid_t loginuid;
+33 −0
Original line number Original line Diff line number Diff line
#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 */
+11 −0
Original line number Original line Diff line number Diff line
@@ -49,6 +49,7 @@
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/ptrace.h>
#include <linux/security.h>
#include <linux/security.h>
#include <linux/task_work.h>
struct linux_binprm;
struct linux_binprm;


/*
/*
@@ -164,8 +165,10 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
 */
 */
static inline void set_notify_resume(struct task_struct *task)
static inline void set_notify_resume(struct task_struct *task)
{
{
#ifdef TIF_NOTIFY_RESUME
	if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_RESUME))
	if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_RESUME))
		kick_process(task);
		kick_process(task);
#endif
}
}


/**
/**
@@ -185,6 +188,14 @@ static inline void tracehook_notify_resume(struct pt_regs *regs)
{
{
	if (current->replacement_session_keyring)
	if (current->replacement_session_keyring)
		key_replace_session_keyring();
		key_replace_session_keyring();
	/*
	 * The caller just cleared TIF_NOTIFY_RESUME. This barrier
	 * pairs with task_work_add()->set_notify_resume() after
	 * hlist_add_head(task->task_works);
	 */
	smp_mb__after_clear_bit();
	if (unlikely(!hlist_empty(&current->task_works)))
		task_work_run();
}
}


#endif	/* <linux/tracehook.h> */
#endif	/* <linux/tracehook.h> */
+1 −1
Original line number Original line Diff line number Diff line
@@ -5,7 +5,7 @@
obj-y     = fork.o exec_domain.o panic.o printk.o \
obj-y     = fork.o exec_domain.o panic.o printk.o \
	    cpu.o exit.o itimer.o time.o softirq.o resource.o \
	    cpu.o exit.o itimer.o time.o softirq.o resource.o \
	    sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \
	    sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \
	    signal.o sys.o kmod.o workqueue.o pid.o \
	    signal.o sys.o kmod.o workqueue.o pid.o task_work.o \
	    rcupdate.o extable.o params.o posix-timers.o \
	    rcupdate.o extable.o params.o posix-timers.o \
	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
	    hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
	    hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
+4 −1
Original line number Original line Diff line number Diff line
@@ -946,11 +946,14 @@ void do_exit(long code)
	exit_signals(tsk);  /* sets PF_EXITING */
	exit_signals(tsk);  /* sets PF_EXITING */
	/*
	/*
	 * tsk->flags are checked in the futex code to protect against
	 * tsk->flags are checked in the futex code to protect against
	 * an exiting task cleaning up the robust pi futexes.
	 * an exiting task cleaning up the robust pi futexes, and in
	 * task_work_add() to avoid the race with exit_task_work().
	 */
	 */
	smp_mb();
	smp_mb();
	raw_spin_unlock_wait(&tsk->pi_lock);
	raw_spin_unlock_wait(&tsk->pi_lock);


	exit_task_work(tsk);

	exit_irq_thread();
	exit_irq_thread();


	if (unlikely(in_atomic()))
	if (unlikely(in_atomic()))
Loading