summaryrefslogtreecommitdiff
path: root/include/linux/context_tracking.h
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2013-02-24 01:19:14 +0100
committerFrederic Weisbecker <fweisbec@gmail.com>2013-03-07 17:10:11 +0100
commit6c1e0256fad84a843d915414e4b5973b7443d48d (patch)
treed93a2f27817f05c67840d947e0f9eb7f31fd24aa /include/linux/context_tracking.h
parent56dd9470d7c8734f055da2a6bac553caf4a468eb (diff)
context_tracking: Restore correct previous context state on exception exit
On exception exit, we restore the previous context tracking state based on the regs of the interrupted frame. Iff that frame is in user mode as stated by user_mode() helper, we restore the context tracking user mode. However there is a tiny chunck of low level arch code after we pass through user_enter() and until the CPU eventually resumes userspace. If an exception happens in this tiny area, exception_enter() correctly exits the context tracking user mode but exception_exit() won't restore it because of the value returned by user_mode(regs). As a result we may return to userspace with the wrong context tracking state. To fix this, change exception_enter() to return the context tracking state prior to its call and pass this saved state to exception_exit(). This restores the real context tracking state of the interrupted frame. (May be this patch was suggested to me, I don't recall exactly. If so, sorry for the missing credit). Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Li Zhong <zhong@linux.vnet.ibm.com> Cc: Kevin Hilman <khilman@linaro.org> Cc: Mats Liljegren <mats.liljegren@enea.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ingo Molnar <mingo@kernel.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Namhyung Kim <namhyung.kim@lge.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'include/linux/context_tracking.h')
-rw-r--r--include/linux/context_tracking.h19
1 files changed, 12 insertions, 7 deletions
diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h
index 5a69273e93e6..365f4a61bf04 100644
--- a/include/linux/context_tracking.h
+++ b/include/linux/context_tracking.h
@@ -5,7 +5,6 @@
#include <linux/percpu.h>
#include <asm/ptrace.h>
-#ifdef CONFIG_CONTEXT_TRACKING
struct context_tracking {
/*
* When active is false, probes are unset in order
@@ -14,12 +13,13 @@ struct context_tracking {
* may be further optimized using static keys.
*/
bool active;
- enum {
+ enum ctx_state {
IN_KERNEL = 0,
IN_USER,
} state;
};
+#ifdef CONFIG_CONTEXT_TRACKING
DECLARE_PER_CPU(struct context_tracking, context_tracking);
static inline bool context_tracking_in_user(void)
@@ -35,14 +35,19 @@ static inline bool context_tracking_active(void)
extern void user_enter(void);
extern void user_exit(void);
-static inline void exception_enter(struct pt_regs *regs)
+static inline enum ctx_state exception_enter(void)
{
+ enum ctx_state prev_ctx;
+
+ prev_ctx = this_cpu_read(context_tracking.state);
user_exit();
+
+ return prev_ctx;
}
-static inline void exception_exit(struct pt_regs *regs)
+static inline void exception_exit(enum ctx_state prev_ctx)
{
- if (user_mode(regs))
+ if (prev_ctx == IN_USER)
user_enter();
}
@@ -52,8 +57,8 @@ extern void context_tracking_task_switch(struct task_struct *prev,
static inline bool context_tracking_in_user(void) { return false; }
static inline void user_enter(void) { }
static inline void user_exit(void) { }
-static inline void exception_enter(struct pt_regs *regs) { }
-static inline void exception_exit(struct pt_regs *regs) { }
+static inline enum ctx_state exception_enter(void) { return 0; }
+static inline void exception_exit(enum ctx_state prev_ctx) { }
static inline void context_tracking_task_switch(struct task_struct *prev,
struct task_struct *next) { }
#endif /* !CONFIG_CONTEXT_TRACKING */