diff options
-rw-r--r-- | arch/Kconfig | 10 | ||||
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/thread_info.h | 7 | ||||
-rw-r--r-- | arch/x86/kernel/process.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/signal.c | 3 | ||||
-rw-r--r-- | include/linux/user-return-notifier.h | 42 | ||||
-rw-r--r-- | kernel/user-return-notifier.c | 46 |
7 files changed, 109 insertions, 2 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index 7f418bbc261a..4e312fffbfd7 100644 --- a/arch/Kconfig +++ b/arch/Kconfig | |||
@@ -83,6 +83,13 @@ config KRETPROBES | |||
83 | def_bool y | 83 | def_bool y |
84 | depends on KPROBES && HAVE_KRETPROBES | 84 | depends on KPROBES && HAVE_KRETPROBES |
85 | 85 | ||
86 | config USER_RETURN_NOTIFIER | ||
87 | bool | ||
88 | depends on HAVE_USER_RETURN_NOTIFIER | ||
89 | help | ||
90 | Provide a kernel-internal notification when a cpu is about to | ||
91 | switch to user mode. | ||
92 | |||
86 | config HAVE_IOREMAP_PROT | 93 | config HAVE_IOREMAP_PROT |
87 | bool | 94 | bool |
88 | 95 | ||
@@ -126,4 +133,7 @@ config HAVE_DMA_API_DEBUG | |||
126 | config HAVE_DEFAULT_NO_SPIN_MUTEXES | 133 | config HAVE_DEFAULT_NO_SPIN_MUTEXES |
127 | bool | 134 | bool |
128 | 135 | ||
136 | config HAVE_USER_RETURN_NOTIFIER | ||
137 | bool | ||
138 | |||
129 | source "kernel/gcov/Kconfig" | 139 | source "kernel/gcov/Kconfig" |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 8da93745c087..1df175d15aa8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -50,6 +50,7 @@ config X86 | |||
50 | select HAVE_KERNEL_BZIP2 | 50 | select HAVE_KERNEL_BZIP2 |
51 | select HAVE_KERNEL_LZMA | 51 | select HAVE_KERNEL_LZMA |
52 | select HAVE_ARCH_KMEMCHECK | 52 | select HAVE_ARCH_KMEMCHECK |
53 | select HAVE_USER_RETURN_NOTIFIER | ||
53 | 54 | ||
54 | config OUTPUT_FORMAT | 55 | config OUTPUT_FORMAT |
55 | string | 56 | string |
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index d27d0a2fec4c..375c917c37d2 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h | |||
@@ -83,6 +83,7 @@ struct thread_info { | |||
83 | #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ | 83 | #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ |
84 | #define TIF_SECCOMP 8 /* secure computing */ | 84 | #define TIF_SECCOMP 8 /* secure computing */ |
85 | #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ | 85 | #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ |
86 | #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ | ||
86 | #define TIF_NOTSC 16 /* TSC is not accessible in userland */ | 87 | #define TIF_NOTSC 16 /* TSC is not accessible in userland */ |
87 | #define TIF_IA32 17 /* 32bit process */ | 88 | #define TIF_IA32 17 /* 32bit process */ |
88 | #define TIF_FORK 18 /* ret_from_fork */ | 89 | #define TIF_FORK 18 /* ret_from_fork */ |
@@ -107,6 +108,7 @@ struct thread_info { | |||
107 | #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) | 108 | #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) |
108 | #define _TIF_SECCOMP (1 << TIF_SECCOMP) | 109 | #define _TIF_SECCOMP (1 << TIF_SECCOMP) |
109 | #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) | 110 | #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) |
111 | #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) | ||
110 | #define _TIF_NOTSC (1 << TIF_NOTSC) | 112 | #define _TIF_NOTSC (1 << TIF_NOTSC) |
111 | #define _TIF_IA32 (1 << TIF_IA32) | 113 | #define _TIF_IA32 (1 << TIF_IA32) |
112 | #define _TIF_FORK (1 << TIF_FORK) | 114 | #define _TIF_FORK (1 << TIF_FORK) |
@@ -142,13 +144,14 @@ struct thread_info { | |||
142 | 144 | ||
143 | /* Only used for 64 bit */ | 145 | /* Only used for 64 bit */ |
144 | #define _TIF_DO_NOTIFY_MASK \ | 146 | #define _TIF_DO_NOTIFY_MASK \ |
145 | (_TIF_SIGPENDING|_TIF_MCE_NOTIFY|_TIF_NOTIFY_RESUME) | 147 | (_TIF_SIGPENDING | _TIF_MCE_NOTIFY | _TIF_NOTIFY_RESUME | \ |
148 | _TIF_USER_RETURN_NOTIFY) | ||
146 | 149 | ||
147 | /* flags to check in __switch_to() */ | 150 | /* flags to check in __switch_to() */ |
148 | #define _TIF_WORK_CTXSW \ | 151 | #define _TIF_WORK_CTXSW \ |
149 | (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_NOTSC) | 152 | (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_NOTSC) |
150 | 153 | ||
151 | #define _TIF_WORK_CTXSW_PREV _TIF_WORK_CTXSW | 154 | #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) |
152 | #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) | 155 | #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) |
153 | 156 | ||
154 | #define PREEMPT_ACTIVE 0x10000000 | 157 | #define PREEMPT_ACTIVE 0x10000000 |
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 5284cd2b5776..e51b056fc88f 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/pm.h> | 9 | #include <linux/pm.h> |
10 | #include <linux/clockchips.h> | 10 | #include <linux/clockchips.h> |
11 | #include <linux/random.h> | 11 | #include <linux/random.h> |
12 | #include <linux/user-return-notifier.h> | ||
12 | #include <trace/events/power.h> | 13 | #include <trace/events/power.h> |
13 | #include <asm/system.h> | 14 | #include <asm/system.h> |
14 | #include <asm/apic.h> | 15 | #include <asm/apic.h> |
@@ -224,6 +225,7 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, | |||
224 | */ | 225 | */ |
225 | memset(tss->io_bitmap, 0xff, prev->io_bitmap_max); | 226 | memset(tss->io_bitmap, 0xff, prev->io_bitmap_max); |
226 | } | 227 | } |
228 | propagate_user_return_notify(prev_p, next_p); | ||
227 | } | 229 | } |
228 | 230 | ||
229 | int sys_fork(struct pt_regs *regs) | 231 | int sys_fork(struct pt_regs *regs) |
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 6a44a76055ad..c49f90f7957a 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/stddef.h> | 19 | #include <linux/stddef.h> |
20 | #include <linux/personality.h> | 20 | #include <linux/personality.h> |
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/user-return-notifier.h> | ||
22 | 23 | ||
23 | #include <asm/processor.h> | 24 | #include <asm/processor.h> |
24 | #include <asm/ucontext.h> | 25 | #include <asm/ucontext.h> |
@@ -872,6 +873,8 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) | |||
872 | if (current->replacement_session_keyring) | 873 | if (current->replacement_session_keyring) |
873 | key_replace_session_keyring(); | 874 | key_replace_session_keyring(); |
874 | } | 875 | } |
876 | if (thread_info_flags & _TIF_USER_RETURN_NOTIFY) | ||
877 | fire_user_return_notifiers(); | ||
875 | 878 | ||
876 | #ifdef CONFIG_X86_32 | 879 | #ifdef CONFIG_X86_32 |
877 | clear_thread_flag(TIF_IRET); | 880 | clear_thread_flag(TIF_IRET); |
diff --git a/include/linux/user-return-notifier.h b/include/linux/user-return-notifier.h new file mode 100644 index 000000000000..b6ac056291d7 --- /dev/null +++ b/include/linux/user-return-notifier.h | |||
@@ -0,0 +1,42 @@ | |||
1 | #ifndef _LINUX_USER_RETURN_NOTIFIER_H | ||
2 | #define _LINUX_USER_RETURN_NOTIFIER_H | ||
3 | |||
4 | #ifdef CONFIG_USER_RETURN_NOTIFIER | ||
5 | |||
6 | #include <linux/list.h> | ||
7 | #include <linux/sched.h> | ||
8 | |||
9 | struct user_return_notifier { | ||
10 | void (*on_user_return)(struct user_return_notifier *urn); | ||
11 | struct hlist_node link; | ||
12 | }; | ||
13 | |||
14 | |||
15 | void user_return_notifier_register(struct user_return_notifier *urn); | ||
16 | void user_return_notifier_unregister(struct user_return_notifier *urn); | ||
17 | |||
18 | static inline void propagate_user_return_notify(struct task_struct *prev, | ||
19 | struct task_struct *next) | ||
20 | { | ||
21 | if (test_tsk_thread_flag(prev, TIF_USER_RETURN_NOTIFY)) { | ||
22 | clear_tsk_thread_flag(prev, TIF_USER_RETURN_NOTIFY); | ||
23 | set_tsk_thread_flag(next, TIF_USER_RETURN_NOTIFY); | ||
24 | } | ||
25 | } | ||
26 | |||
27 | void fire_user_return_notifiers(void); | ||
28 | |||
29 | #else | ||
30 | |||
31 | struct user_return_notifier {}; | ||
32 | |||
33 | static inline void propagate_user_return_notify(struct task_struct *prev, | ||
34 | struct task_struct *next) | ||
35 | { | ||
36 | } | ||
37 | |||
38 | static inline void fire_user_return_notifiers(void) {} | ||
39 | |||
40 | #endif | ||
41 | |||
42 | #endif | ||
diff --git a/kernel/user-return-notifier.c b/kernel/user-return-notifier.c new file mode 100644 index 000000000000..530ccb816513 --- /dev/null +++ b/kernel/user-return-notifier.c | |||
@@ -0,0 +1,46 @@ | |||
1 | |||
2 | #include <linux/user-return-notifier.h> | ||
3 | #include <linux/percpu.h> | ||
4 | #include <linux/sched.h> | ||
5 | #include <linux/module.h> | ||
6 | |||
7 | static DEFINE_PER_CPU(struct hlist_head, return_notifier_list); | ||
8 | |||
9 | #define URN_LIST_HEAD per_cpu(return_notifier_list, raw_smp_processor_id()) | ||
10 | |||
11 | /* | ||
12 | * Request a notification when the current cpu returns to userspace. Must be | ||
13 | * called in atomic context. The notifier will also be called in atomic | ||
14 | * context. | ||
15 | */ | ||
16 | void user_return_notifier_register(struct user_return_notifier *urn) | ||
17 | { | ||
18 | set_tsk_thread_flag(current, TIF_USER_RETURN_NOTIFY); | ||
19 | hlist_add_head(&urn->link, &URN_LIST_HEAD); | ||
20 | } | ||
21 | EXPORT_SYMBOL_GPL(user_return_notifier_register); | ||
22 | |||
23 | /* | ||
24 | * Removes a registered user return notifier. Must be called from atomic | ||
25 | * context, and from the same cpu registration occured in. | ||
26 | */ | ||
27 | void user_return_notifier_unregister(struct user_return_notifier *urn) | ||
28 | { | ||
29 | hlist_del(&urn->link); | ||
30 | if (hlist_empty(&URN_LIST_HEAD)) | ||
31 | clear_tsk_thread_flag(current, TIF_USER_RETURN_NOTIFY); | ||
32 | } | ||
33 | EXPORT_SYMBOL_GPL(user_return_notifier_unregister); | ||
34 | |||
35 | /* Calls registered user return notifiers */ | ||
36 | void fire_user_return_notifiers(void) | ||
37 | { | ||
38 | struct user_return_notifier *urn; | ||
39 | struct hlist_node *tmp1, *tmp2; | ||
40 | struct hlist_head *head; | ||
41 | |||
42 | head = &get_cpu_var(return_notifier_list); | ||
43 | hlist_for_each_entry_safe(urn, tmp1, tmp2, head, link) | ||
44 | urn->on_user_return(urn); | ||
45 | put_cpu_var(); | ||
46 | } | ||