Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal
Pull second pile of signal handling patches from Al Viro:
"This one is just task_work_add() series + remaining prereqs for it.
There probably will be another pull request from that tree this
cycle - at least for helpers, to get them out of the way for per-arch
fixes remaining in the tree."
Fix trivial conflict in kernel/irq/manage.c: the merge of Andrew's pile
had brought in commit 97fd75b7b8
("kernel/irq/manage.c: use the
pr_foo() infrastructure to prefix printks") which changed one of the
pr_err() calls that this merge moves around.
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal:
keys: kill task_struct->replacement_session_keyring
keys: kill the dummy key_replace_session_keyring()
keys: change keyctl_session_to_parent() to use task_work_add()
genirq: reimplement exit_irq_thread() hook via task_work_add()
task_work_add: generic process-context callbacks
avr32: missed _TIF_NOTIFY_RESUME on one of do_notify_resume callers
parisc: need to check NOTIFY_RESUME when exiting from syscall
move key_repace_session_keyring() into tracehook_notify_resume()
TIF_NOTIFY_RESUME is defined on all targets now
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
obj-y = fork.o exec_domain.o panic.o printk.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 \
|
||||
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 \
|
||||
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 \
|
||||
|
@@ -207,13 +207,6 @@ void exit_creds(struct task_struct *tsk)
|
||||
validate_creds(cred);
|
||||
alter_cred_subscribers(cred, -1);
|
||||
put_cred(cred);
|
||||
|
||||
cred = (struct cred *) tsk->replacement_session_keyring;
|
||||
if (cred) {
|
||||
tsk->replacement_session_keyring = NULL;
|
||||
validate_creds(cred);
|
||||
put_cred(cred);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,8 +389,6 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
p->replacement_session_keyring = NULL;
|
||||
|
||||
if (
|
||||
#ifdef CONFIG_KEYS
|
||||
!p->cred->thread_keyring &&
|
||||
|
@@ -946,12 +946,13 @@ void do_exit(long code)
|
||||
exit_signals(tsk); /* sets PF_EXITING */
|
||||
/*
|
||||
* 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();
|
||||
raw_spin_unlock_wait(&tsk->pi_lock);
|
||||
|
||||
exit_irq_thread();
|
||||
exit_task_work(tsk);
|
||||
|
||||
if (unlikely(in_atomic()))
|
||||
printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n",
|
||||
|
@@ -1415,6 +1415,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
*/
|
||||
p->group_leader = p;
|
||||
INIT_LIST_HEAD(&p->thread_group);
|
||||
INIT_HLIST_HEAD(&p->task_works);
|
||||
|
||||
/* Now that the task is set up, run cgroup callbacks if
|
||||
* necessary. We need to run them before the task is visible
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/task_work.h>
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
@@ -775,11 +776,39 @@ static void wake_threads_waitq(struct irq_desc *desc)
|
||||
wake_up(&desc->wait_for_threads);
|
||||
}
|
||||
|
||||
static void irq_thread_dtor(struct task_work *unused)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct irq_desc *desc;
|
||||
struct irqaction *action;
|
||||
|
||||
if (WARN_ON_ONCE(!(current->flags & PF_EXITING)))
|
||||
return;
|
||||
|
||||
action = kthread_data(tsk);
|
||||
|
||||
pr_err("exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
|
||||
tsk->comm ? tsk->comm : "", tsk->pid, action->irq);
|
||||
|
||||
|
||||
desc = irq_to_desc(action->irq);
|
||||
/*
|
||||
* If IRQTF_RUNTHREAD is set, we need to decrement
|
||||
* desc->threads_active and wake possible waiters.
|
||||
*/
|
||||
if (test_and_clear_bit(IRQTF_RUNTHREAD, &action->thread_flags))
|
||||
wake_threads_waitq(desc);
|
||||
|
||||
/* Prevent a stale desc->threads_oneshot */
|
||||
irq_finalize_oneshot(desc, action);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler thread
|
||||
*/
|
||||
static int irq_thread(void *data)
|
||||
{
|
||||
struct task_work on_exit_work;
|
||||
static const struct sched_param param = {
|
||||
.sched_priority = MAX_USER_RT_PRIO/2,
|
||||
};
|
||||
@@ -795,7 +824,9 @@ static int irq_thread(void *data)
|
||||
handler_fn = irq_thread_fn;
|
||||
|
||||
sched_setscheduler(current, SCHED_FIFO, ¶m);
|
||||
current->irq_thread = 1;
|
||||
|
||||
init_task_work(&on_exit_work, irq_thread_dtor, NULL);
|
||||
task_work_add(current, &on_exit_work, false);
|
||||
|
||||
while (!irq_wait_for_interrupt(action)) {
|
||||
irqreturn_t action_ret;
|
||||
@@ -817,44 +848,11 @@ static int irq_thread(void *data)
|
||||
* cannot touch the oneshot mask at this point anymore as
|
||||
* __setup_irq() might have given out currents thread_mask
|
||||
* again.
|
||||
*
|
||||
* Clear irq_thread. Otherwise exit_irq_thread() would make
|
||||
* fuzz about an active irq thread going into nirvana.
|
||||
*/
|
||||
current->irq_thread = 0;
|
||||
task_work_cancel(current, irq_thread_dtor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from do_exit()
|
||||
*/
|
||||
void exit_irq_thread(void)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct irq_desc *desc;
|
||||
struct irqaction *action;
|
||||
|
||||
if (!tsk->irq_thread)
|
||||
return;
|
||||
|
||||
action = kthread_data(tsk);
|
||||
|
||||
pr_err("exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
|
||||
tsk->comm ? tsk->comm : "", tsk->pid, action->irq);
|
||||
|
||||
desc = irq_to_desc(action->irq);
|
||||
|
||||
/*
|
||||
* If IRQTF_RUNTHREAD is set, we need to decrement
|
||||
* desc->threads_active and wake possible waiters.
|
||||
*/
|
||||
if (test_and_clear_bit(IRQTF_RUNTHREAD, &action->thread_flags))
|
||||
wake_threads_waitq(desc);
|
||||
|
||||
/* Prevent a stale desc->threads_oneshot */
|
||||
irq_finalize_oneshot(desc, action);
|
||||
}
|
||||
|
||||
static void irq_setup_forced_threading(struct irqaction *new)
|
||||
{
|
||||
if (!force_irqthreads)
|
||||
|
84
kernel/task_work.c
Normal file
84
kernel/task_work.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user