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 10ab2d74ecbbede2764c8bf8cbcca14f19f418a0..f6db3032ddf0b5b7283c4dd83b135062719e88db 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 17fc36c41cff6c155802fab60e3b04a3b5efd135..63f327dd519814b28a5c092b203e089ec4d732ea 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 169268c40ae2552df9430128e4ae6e6964249989..5e6beb2597a837d53aed33e052f6772bc757653f 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 ae386c304beefe153fb162e2b6909edc84dd5ef4..e7595ef74f51f76a1b86723569e47441b02cd4bc 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 e5bbc1a5edc242dae7221168f771bbad704872d3..fc9ecce8b6cee28d4352f1e3f06ddaac30682bcc 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 cf37478c1169c59411bc6926142c020a6348ec26..9493f0bbf0a6f1b045dd18ee443fea83ca8b4aea 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 d114ad3da9b15f95ccc5a68b736d5e80bae75af7..58d44ee1a71f70885aef328fdf6a6af32d6c7b58 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 8cf5dca01758f75e63041d2c81edf1d8542bbb0c..595bf1e5a5dc0262bfc6897fb3b2999ab259fdc7 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 d4b0555d29047851d9a80d9ae020f30eecc1a370..e58992ad789ef0a0822f52616c03aec1126fa699 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 434866eb0f1cf36d639c5e14367458bf272522df..21a3018cb9bf78d9e480737ec87ccd885b2de422 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 5e0e86ddb12f7801ec0a1f7e5579dae48168d5a4..dd6fc14497419dca929be82a44b55ce638bc4705 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 f54d96993ea187686f7264fdab3867d5ffc821ca..64804f1f5141dcb2a82e21784cfb4598d803b0ef 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 d9f3d1900eed029a5044fb05845dceeccb261844..973eec60cad45527a872181e60d9c81d6630fa98 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 7f4c7bef1642e0107afb3ffc7bbb02ce87b74585..5d796e32786ea55bb9960e875b55d1e8c0757c3e 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 17f6ee30ad0d604da53fca7da7569bf4006d3d35..8a6e6d116ab0399b2697d6a67660c23107229bf5 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 890cf91767cc8486b4fd004cf8ca6e44c9b94a28..b8b6aa1a68379d7d563baa2263858ec640d0fa0f 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 e970743251ae3c922fb4ff4f9722f4dc596f1f6f..9ae61152295348e7d5bc4e05e731884703d8018b 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 07ef351edd57da0b4fe576a6eed02026b148e890..c7fbc96472f3f9e8ae8013fb72528d9f81d584d8 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 4b9cb0d546d132fb39c5ba57d3986f3b109dff48..e7a7cd3e112009272fbe584de682d856e7e52ac5 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 651c5963662b68ed098c04d1dbdc512f06f2fac1..bfc3ec1382fbf4edb3530857e21152d0539cd64b 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 f626232e216c0ca100f14c1cbeed52c8696c578f..42a6e8b47f06ca1ff8c7dd3009e63107f8dacead 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 d4a49011c48a58d4311b182f6015efeb67a55e6d..302838d3acf6ce890e56a06b57e4a337421b1089 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 cb4172c8af7d81c90e373a7c9a7ecb61a0bbd3a9..9d7bfd66f189c4024665e6f486aea1fded24b3d8 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 b589a354c069aec0ee66d231b7790c7c0d9b3717..aa6428430842a25399c06e7df21c4dd9ee0b9803 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 2b7e849f7c6528b1da91146f3f515ccbf21370c3..6b42e8622d1280a98f4bf8daee177f8328920d55 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 eafaab486b2d24465a0911e46a08c35ce17d3d29..c82cf1cc39652e719469a6f291bd74a2e6b8bb33 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 ba1023d8a0218ca3a33918cf3a256a592a77fb5f..6be7991505019a30ce3ff6268c275a931a088644 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 3a2235e0abc3e18dec7862208364bb8de2d6916c..ccb9a9d283f165760b20fec6bb3b821f814b0a37 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 7754df6ef7d45444dea14b6ebd93a035e54309d6..28782ad47b93ee5d778cfa225776f87cebbb5719 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 965dfda0fd5e442fa88a13e5aeff0a9b98e73029..2e937a5ad53154b4ff3a047e181894ec1a44633e 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 c5e4ec0598d24b8ce1406eb73ce83cb580da014b..ea7e17778a75546d251b040e30819d26f8acbac3 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 c91171599cb68825709aa12a9d79e80026bd9dfb..e68a8e53bb59acf87c2dd07259f77ce491d5ec1e 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 5231800770e1ea3b8cc0a9b081668b4edbdf55b8..4cd22ed627efd79205860b0efa6dde15a07897ed 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 f45c0b280b5d39873aaca3a3d67b1a01362adba8..660c8ae93471cfb1a150936fda19e8f5a0945ef8 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 51bd91d911c3b3233e90301a4092a84750827776..6a4d82bedb03d4f6e9742069c40324c0441265f0 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 80be6ca0cc753c3bd8ff4c8b6dba71dca8f07657..6f3d0ae044b24769c1caa61346174bc44a3684bc 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 430557ea488f3625243455afcdd6b2f9f481ac19..de728ac50d821b9f38340534a4ba6202137d55a2 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 6d85655353e919fd56e0d2cc65cb36862e328a8c..34867cc5b42a77f325c204bb2fd09e1fabe38955 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 c55b61ab6d646c0dbbd3d9042bdc4da8405e1f38..ab5211b9e622cf94d07b7bfb4ccfd9bac85e7b79 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 7c475cd3f6e69800126548aa46c78d4e59d92e03..ea0c6c2ae6f747d0bd8dff198e5e1f6b6934f050 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 f711b094ed412e723207e5d75ceb86a0c81e4439..3dcbf86b0d31b9c7c9c889c80dacf34f9b33adc6 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 21907ea35b15c118515eb03dea87efab7f8ad9f8..0f5b3f0272995dc7057f1306c346468174d3e464 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 d71056db7b67501a085fd4a8feda5c841dd83094..4ad54eea1ea45554d5d931497671fdb32a33660b 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;