Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Jun 2012 01:47:30 +0000 (18:47 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 1 Jun 2012 01:47:30 +0000 (18:47 -0700)
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 97fd75b7b8e0 ("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

45 files changed:
arch/alpha/kernel/signal.c
arch/arm/kernel/signal.c
arch/avr32/kernel/entry-avr32b.S
arch/avr32/kernel/signal.c
arch/blackfin/kernel/signal.c
arch/c6x/kernel/signal.c
arch/cris/kernel/ptrace.c
arch/frv/kernel/signal.c
arch/h8300/kernel/signal.c
arch/hexagon/kernel/signal.c
arch/ia64/kernel/process.c
arch/m32r/kernel/signal.c
arch/m68k/kernel/signal.c
arch/microblaze/kernel/signal.c
arch/mips/kernel/signal.c
arch/mn10300/kernel/signal.c
arch/openrisc/kernel/signal.c
arch/parisc/kernel/entry.S
arch/parisc/kernel/signal.c
arch/powerpc/kernel/signal.c
arch/s390/kernel/signal.c
arch/score/kernel/signal.c
arch/sh/kernel/signal_32.c
arch/sh/kernel/signal_64.c
arch/sparc/kernel/signal_32.c
arch/sparc/kernel/signal_64.c
arch/tile/kernel/process.c
arch/um/kernel/process.c
arch/unicore32/kernel/signal.c
arch/x86/kernel/signal.c
arch/xtensa/kernel/signal.c
include/linux/interrupt.h
include/linux/key.h
include/linux/sched.h
include/linux/task_work.h [new file with mode: 0644]
include/linux/tracehook.h
kernel/Makefile
kernel/cred.c
kernel/exit.c
kernel/fork.c
kernel/irq/manage.c
kernel/task_work.c [new file with mode: 0644]
security/keys/internal.h
security/keys/keyctl.c
security/keys/process_keys.c

index 10ab2d7..f6db303 100644 (file)
@@ -590,7 +590,5 @@ do_notify_resume(struct pt_regs *regs, struct switch_stack *sw,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 17fc36c..63f327d 100644 (file)
@@ -679,7 +679,5 @@ do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
        if (thread_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 169268c..5e6beb2 100644 (file)
@@ -587,7 +587,7 @@ fault_exit_work:
        ld.w    r1, r0[TI_flags]
        rjmp    fault_exit_work
 
-1:     mov     r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
+1:     mov     r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | _TIF_NOTIFY_RESUME
        tst     r1, r2
        breq    2f
        unmask_interrupts
index ae386c3..e7595ef 100644 (file)
@@ -321,7 +321,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti)
        if (ti->flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index e5bbc1a..fc9ecce 100644 (file)
@@ -336,8 +336,6 @@ asmlinkage void do_notify_resume(struct pt_regs *regs)
        if (test_thread_flag(TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index cf37478..9493f0b 100644 (file)
@@ -364,7 +364,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags,
        if (thread_info_flags & (1 << TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index d114ad3..58d44ee 100644 (file)
@@ -40,7 +40,5 @@ void do_notify_resume(int canrestart, struct pt_regs *regs,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 8cf5dca..595bf1e 100644 (file)
@@ -562,8 +562,6 @@ asmlinkage void do_notify_resume(__u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(__frame);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 
 } /* end do_notify_resume() */
index d4b0555..e58992a 100644 (file)
@@ -513,7 +513,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 434866e..21a3018 100644 (file)
@@ -273,8 +273,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index 5e0e86d..dd6fc14 100644 (file)
@@ -199,8 +199,6 @@ do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
        if (test_thread_flag(TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(&scr->pt);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 
        /* copy user rbs to kernel rbs */
index f54d969..64804f1 100644 (file)
@@ -383,8 +383,6 @@ void do_notify_resume(struct pt_regs *regs, __u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 
        clear_thread_flag(TIF_IRET);
index d9f3d19..973eec6 100644 (file)
@@ -1193,9 +1193,6 @@ void do_notify_resume(struct pt_regs *regs)
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs);
 
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
index 7f4c7be..5d796e3 100644 (file)
@@ -401,9 +401,6 @@ void do_notify_resume(struct pt_regs *regs, int in_syscall)
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs, in_syscall);
 
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
index 17f6ee3..8a6e6d1 100644 (file)
@@ -636,8 +636,6 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index 890cf91..b8b6aa1 100644 (file)
@@ -554,7 +554,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(current_frame());
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index e970743..9ae6115 100644 (file)
@@ -376,7 +376,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs)
        if (current_thread_info()->flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 07ef351..c7fbc96 100644 (file)
@@ -2032,7 +2032,7 @@ syscall_check_resched:
        .import do_signal,code
 syscall_check_sig:
        LDREG   TI_FLAGS-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r19
-       ldi     (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK), %r26
+       ldi     (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NOTIFY_RESUME), %r26
        and,COND(<>)    %r19, %r26, %r0
        b,n     syscall_restore /* skip past if we've nothing to do */
 
index 4b9cb0d..e7a7cd3 100644 (file)
@@ -638,7 +638,5 @@ void do_notify_resume(struct pt_regs *regs, long in_syscall)
        if (test_thread_flag(TIF_NOTIFY_RESUME)) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 651c596..bfc3ec1 100644 (file)
@@ -193,8 +193,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index f626232..42a6e8b 100644 (file)
@@ -494,6 +494,4 @@ void do_notify_resume(struct pt_regs *regs)
 {
        clear_thread_flag(TIF_NOTIFY_RESUME);
        tracehook_notify_resume(regs);
-       if (current->replacement_session_keyring)
-               key_replace_session_keyring();
 }
index d4a4901..302838d 100644 (file)
@@ -356,7 +356,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index cb4172c..9d7bfd6 100644 (file)
@@ -626,7 +626,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index b589a35..aa64284 100644 (file)
@@ -685,7 +685,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned long thread_info
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
index 2b7e849..6b42e86 100644 (file)
@@ -590,8 +590,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0,
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index eafaab4..c82cf1c 100644 (file)
@@ -607,8 +607,6 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index ba1023d..6be7991 100644 (file)
@@ -565,8 +565,6 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
                return 1;
        }
        if (thread_info_flags & _TIF_SINGLESTEP) {
index 3a2235e..ccb9a9d 100644 (file)
@@ -117,11 +117,8 @@ void interrupt_end(void)
                schedule();
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal();
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(&current->thread.regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
 
 void exit_thread(void)
index 7754df6..28782ad 100644 (file)
@@ -464,8 +464,6 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
        if (thread_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
 }
 
index 965dfda..2e937a5 100644 (file)
@@ -827,8 +827,6 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
        if (thread_info_flags & _TIF_NOTIFY_RESUME) {
                clear_thread_flag(TIF_NOTIFY_RESUME);
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
        }
        if (thread_info_flags & _TIF_USER_RETURN_NOTIFY)
                fire_user_return_notifiers();
index c5e4ec0..ea7e177 100644 (file)
@@ -548,9 +548,6 @@ void do_notify_resume(struct pt_regs *regs)
        if (test_thread_flag(TIF_SIGPENDING))
                do_signal(regs);
 
-       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+       if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
                tracehook_notify_resume(regs);
-               if (current->replacement_session_keyring)
-                       key_replace_session_keyring();
-       }
 }
index c911715..e68a8e5 100644 (file)
@@ -142,8 +142,6 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler,
 extern int __must_check
 request_percpu_irq(unsigned int irq, irq_handler_t handler,
                   const char *devname, void __percpu *percpu_dev_id);
-
-extern void exit_irq_thread(void);
 #else
 
 extern int __must_check
@@ -177,8 +175,6 @@ request_percpu_irq(unsigned int irq, irq_handler_t handler,
 {
        return request_irq(irq, handler, 0, devname, percpu_dev_id);
 }
-
-static inline void exit_irq_thread(void) { }
 #endif
 
 extern void free_irq(unsigned int, void *);
index 5231800..4cd22ed 100644 (file)
@@ -308,9 +308,6 @@ static inline bool key_is_instantiated(const struct key *key)
 #ifdef CONFIG_SYSCTL
 extern ctl_table key_sysctls[];
 #endif
-
-extern void key_replace_session_keyring(void);
-
 /*
  * the userspace interface
  */
@@ -334,7 +331,6 @@ extern void key_init(void);
 #define key_fsuid_changed(t)           do { } while(0)
 #define key_fsgid_changed(t)           do { } while(0)
 #define key_init()                     do { } while(0)
-#define key_replace_session_keyring()  do { } while(0)
 
 #endif /* CONFIG_KEYS */
 #endif /* __KERNEL__ */
index f45c0b2..660c8ae 100644 (file)
@@ -1301,11 +1301,6 @@ struct task_struct {
        unsigned sched_reset_on_fork:1;
        unsigned sched_contributes_to_load:1;
 
-#ifdef CONFIG_GENERIC_HARDIRQS
-       /* IRQ handler threads */
-       unsigned irq_thread:1;
-#endif
-
        pid_t pid;
        pid_t tgid;
 
@@ -1313,10 +1308,9 @@ struct task_struct {
        /* Canary value for the -fstack-protector gcc feature */
        unsigned long stack_canary;
 #endif
-
-       /* 
+       /*
         * pointers to (original) parent process, youngest child, younger sibling,
-        * older sibling, respectively.  (p->father can be replaced with 
+        * older sibling, respectively.  (p->father can be replaced with
         * p->real_parent->pid)
         */
        struct task_struct __rcu *real_parent; /* real parent process */
@@ -1363,8 +1357,6 @@ struct task_struct {
                                         * credentials (COW) */
        const struct cred __rcu *cred;  /* effective (overridable) subjective task
                                         * credentials (COW) */
-       struct cred *replacement_session_keyring; /* for KEYCTL_SESSION_TO_PARENT */
-
        char comm[TASK_COMM_LEN]; /* executable name excluding path
                                     - access with [gs]et_task_comm (which lock
                                       it with task_lock())
@@ -1400,6 +1392,8 @@ struct task_struct {
        int (*notifier)(void *priv);
        void *notifier_data;
        sigset_t *notifier_mask;
+       struct hlist_head task_works;
+
        struct audit_context *audit_context;
 #ifdef CONFIG_AUDITSYSCALL
        uid_t loginuid;
diff --git a/include/linux/task_work.h b/include/linux/task_work.h
new file mode 100644 (file)
index 0000000..294d5d5
--- /dev/null
@@ -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 */
index 51bd91d..6a4d82b 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <linux/security.h>
+#include <linux/task_work.h>
 struct linux_binprm;
 
 /*
@@ -153,7 +154,6 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
                ptrace_notify(SIGTRAP);
 }
 
-#ifdef TIF_NOTIFY_RESUME
 /**
  * set_notify_resume - cause tracehook_notify_resume() to be called
  * @task:              task that will call tracehook_notify_resume()
@@ -165,8 +165,10 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
  */
 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))
                kick_process(task);
+#endif
 }
 
 /**
@@ -184,7 +186,14 @@ static inline void set_notify_resume(struct task_struct *task)
  */
 static inline void tracehook_notify_resume(struct pt_regs *regs)
 {
+       /*
+        * 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 /* TIF_NOTIFY_RESUME */
 
 #endif /* <linux/tracehook.h> */
index 80be6ca..6f3d0ae 100644 (file)
@@ -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 \
index 430557e..de728ac 100644 (file)
@@ -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 &&
index 6d85655..34867cc 100644 (file)
@@ -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",
index c55b61a..ab5211b 100644 (file)
@@ -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
index 7c475cd..ea0c6c2 100644 (file)
@@ -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, &param);
-       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)
diff --git a/kernel/task_work.c b/kernel/task_work.c
new file mode 100644 (file)
index 0000000..82d1c79
--- /dev/null
@@ -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);
+       }
+}
index f711b09..3dcbf86 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/sched.h>
 #include <linux/key-type.h>
+#include <linux/task_work.h>
 
 #ifdef __KDEBUG
 #define kenter(FMT, ...) \
@@ -148,6 +149,7 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
 #define KEY_LOOKUP_FOR_UNLINK  0x04
 
 extern long join_session_keyring(const char *name);
+extern void key_change_session_keyring(struct task_work *twork);
 
 extern struct work_struct key_gc_work;
 extern unsigned key_gc_delay;
index 21907ea..0f5b3f0 100644 (file)
@@ -1454,50 +1454,57 @@ long keyctl_get_security(key_serial_t keyid,
  */
 long keyctl_session_to_parent(void)
 {
-#ifdef TIF_NOTIFY_RESUME
        struct task_struct *me, *parent;
        const struct cred *mycred, *pcred;
-       struct cred *cred, *oldcred;
+       struct task_work *newwork, *oldwork;
        key_ref_t keyring_r;
+       struct cred *cred;
        int ret;
 
        keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK);
        if (IS_ERR(keyring_r))
                return PTR_ERR(keyring_r);
 
+       ret = -ENOMEM;
+       newwork = kmalloc(sizeof(struct task_work), GFP_KERNEL);
+       if (!newwork)
+               goto error_keyring;
+
        /* our parent is going to need a new cred struct, a new tgcred struct
         * and new security data, so we allocate them here to prevent ENOMEM in
         * our parent */
-       ret = -ENOMEM;
        cred = cred_alloc_blank();
        if (!cred)
-               goto error_keyring;
+               goto error_newwork;
 
        cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r);
-       keyring_r = NULL;
+       init_task_work(newwork, key_change_session_keyring, cred);
 
        me = current;
        rcu_read_lock();
        write_lock_irq(&tasklist_lock);
 
-       parent = me->real_parent;
        ret = -EPERM;
+       oldwork = NULL;
+       parent = me->real_parent;
 
        /* the parent mustn't be init and mustn't be a kernel thread */
        if (parent->pid <= 1 || !parent->mm)
-               goto not_permitted;
+               goto unlock;
 
        /* the parent must be single threaded */
        if (!thread_group_empty(parent))
-               goto not_permitted;
+               goto unlock;
 
        /* the parent and the child must have different session keyrings or
         * there's no point */
        mycred = current_cred();
        pcred = __task_cred(parent);
        if (mycred == pcred ||
-           mycred->tgcred->session_keyring == pcred->tgcred->session_keyring)
-               goto already_same;
+           mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) {
+               ret = 0;
+               goto unlock;
+       }
 
        /* the parent must have the same effective ownership and mustn't be
         * SUID/SGID */
@@ -1507,50 +1514,40 @@ long keyctl_session_to_parent(void)
            pcred->gid  != mycred->egid ||
            pcred->egid != mycred->egid ||
            pcred->sgid != mycred->egid)
-               goto not_permitted;
+               goto unlock;
 
        /* the keyrings must have the same UID */
        if ((pcred->tgcred->session_keyring &&
             pcred->tgcred->session_keyring->uid != mycred->euid) ||
            mycred->tgcred->session_keyring->uid != mycred->euid)
-               goto not_permitted;
+               goto unlock;
 
-       /* if there's an already pending keyring replacement, then we replace
-        * that */
-       oldcred = parent->replacement_session_keyring;
+       /* cancel an already pending keyring replacement */
+       oldwork = task_work_cancel(parent, key_change_session_keyring);
 
        /* the replacement session keyring is applied just prior to userspace
         * restarting */
-       parent->replacement_session_keyring = cred;
-       cred = NULL;
-       set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME);
-
+       ret = task_work_add(parent, newwork, true);
+       if (!ret)
+               newwork = NULL;
+unlock:
        write_unlock_irq(&tasklist_lock);
        rcu_read_unlock();
-       if (oldcred)
-               put_cred(oldcred);
-       return 0;
-
-already_same:
-       ret = 0;
-not_permitted:
-       write_unlock_irq(&tasklist_lock);
-       rcu_read_unlock();
-       put_cred(cred);
+       if (oldwork) {
+               put_cred(oldwork->data);
+               kfree(oldwork);
+       }
+       if (newwork) {
+               put_cred(newwork->data);
+               kfree(newwork);
+       }
        return ret;
 
+error_newwork:
+       kfree(newwork);
 error_keyring:
        key_ref_put(keyring_r);
        return ret;
-
-#else /* !TIF_NOTIFY_RESUME */
-       /*
-        * To be removed when TIF_NOTIFY_RESUME has been implemented on
-        * m68k/xtensa
-        */
-#warning TIF_NOTIFY_RESUME not implemented
-       return -EOPNOTSUPP;
-#endif /* !TIF_NOTIFY_RESUME */
 }
 
 /*
index d71056d..4ad54ee 100644 (file)
@@ -834,23 +834,17 @@ error:
  * Replace a process's session keyring on behalf of one of its children when
  * the target  process is about to resume userspace execution.
  */
-void key_replace_session_keyring(void)
+void key_change_session_keyring(struct task_work *twork)
 {
-       const struct cred *old;
-       struct cred *new;
-
-       if (!current->replacement_session_keyring)
-               return;
+       const struct cred *old = current_cred();
+       struct cred *new = twork->data;
 
-       write_lock_irq(&tasklist_lock);
-       new = current->replacement_session_keyring;
-       current->replacement_session_keyring = NULL;
-       write_unlock_irq(&tasklist_lock);
-
-       if (!new)
+       kfree(twork);
+       if (unlikely(current->flags & PF_EXITING)) {
+               put_cred(new);
                return;
+       }
 
-       old = current_cred();
        new->  uid      = old->  uid;
        new-> euid      = old-> euid;
        new-> suid      = old-> suid;