forked from wandboard-org/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
context_tracking: Add comments on interface and internals
This subsystem lacks many explanations on its purpose and design. Add these missing comments. v4: Document function parameter to be more kernel-doc friendly, as per Namhyung suggestion. Reported-by: Andrew Morton <[email protected]> Signed-off-by: Frederic Weisbecker <[email protected]> Cc: Alessio Igor Bogani <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Chris Metcalf <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: Geoff Levand <[email protected]> Cc: Gilad Ben Yossef <[email protected]> Cc: Hakan Akkan <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Li Zhong <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Paul E. McKenney <[email protected]> Cc: Paul Gortmaker <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Thomas Gleixner <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]>
- Loading branch information
Showing
1 changed file
with
65 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,19 @@ | ||
/* | ||
* Context tracking: Probe on high level context boundaries such as kernel | ||
* and userspace. This includes syscalls and exceptions entry/exit. | ||
* | ||
* This is used by RCU to remove its dependency on the timer tick while a CPU | ||
* runs in userspace. | ||
* | ||
* Started by Frederic Weisbecker: | ||
* | ||
* Copyright (C) 2012 Red Hat, Inc., Frederic Weisbecker <[email protected]> | ||
* | ||
* Many thanks to Gilad Ben-Yossef, Paul McKenney, Ingo Molnar, Andrew Morton, | ||
* Steven Rostedt, Peter Zijlstra for suggestions and improvements. | ||
* | ||
*/ | ||
|
||
#include <linux/context_tracking.h> | ||
#include <linux/rcupdate.h> | ||
#include <linux/sched.h> | ||
|
@@ -6,8 +22,8 @@ | |
|
||
struct context_tracking { | ||
/* | ||
* When active is false, hooks are not set to | ||
* minimize overhead: TIF flags are cleared | ||
* When active is false, probes are unset in order | ||
* to minimize overhead: TIF flags are cleared | ||
* and calls to user_enter/exit are ignored. This | ||
* may be further optimized using static keys. | ||
*/ | ||
|
@@ -24,6 +40,15 @@ static DEFINE_PER_CPU(struct context_tracking, context_tracking) = { | |
#endif | ||
}; | ||
|
||
/** | ||
* user_enter - Inform the context tracking that the CPU is going to | ||
* enter userspace mode. | ||
* | ||
* This function must be called right before we switch from the kernel | ||
* to userspace, when it's guaranteed the remaining kernel instructions | ||
* to execute won't use any RCU read side critical section because this | ||
* function sets RCU in extended quiescent state. | ||
*/ | ||
void user_enter(void) | ||
{ | ||
unsigned long flags; | ||
|
@@ -39,40 +64,70 @@ void user_enter(void) | |
if (in_interrupt()) | ||
return; | ||
|
||
/* Kernel threads aren't supposed to go to userspace */ | ||
WARN_ON_ONCE(!current->mm); | ||
|
||
local_irq_save(flags); | ||
if (__this_cpu_read(context_tracking.active) && | ||
__this_cpu_read(context_tracking.state) != IN_USER) { | ||
__this_cpu_write(context_tracking.state, IN_USER); | ||
/* | ||
* At this stage, only low level arch entry code remains and | ||
* then we'll run in userspace. We can assume there won't be | ||
* any RCU read-side critical section until the next call to | ||
* user_exit() or rcu_irq_enter(). Let's remove RCU's dependency | ||
* on the tick. | ||
*/ | ||
rcu_user_enter(); | ||
} | ||
local_irq_restore(flags); | ||
} | ||
|
||
|
||
/** | ||
* user_exit - Inform the context tracking that the CPU is | ||
* exiting userspace mode and entering the kernel. | ||
* | ||
* This function must be called after we entered the kernel from userspace | ||
* before any use of RCU read side critical section. This potentially include | ||
* any high level kernel code like syscalls, exceptions, signal handling, etc... | ||
* | ||
* This call supports re-entrancy. This way it can be called from any exception | ||
* handler without needing to know if we came from userspace or not. | ||
*/ | ||
void user_exit(void) | ||
{ | ||
unsigned long flags; | ||
|
||
/* | ||
* Some contexts may involve an exception occuring in an irq, | ||
* leading to that nesting: | ||
* rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit() | ||
* This would mess up the dyntick_nesting count though. And rcu_irq_*() | ||
* helpers are enough to protect RCU uses inside the exception. So | ||
* just return immediately if we detect we are in an IRQ. | ||
*/ | ||
if (in_interrupt()) | ||
return; | ||
|
||
local_irq_save(flags); | ||
if (__this_cpu_read(context_tracking.state) == IN_USER) { | ||
__this_cpu_write(context_tracking.state, IN_KERNEL); | ||
/* | ||
* We are going to run code that may use RCU. Inform | ||
* RCU core about that (ie: we may need the tick again). | ||
*/ | ||
rcu_user_exit(); | ||
} | ||
local_irq_restore(flags); | ||
} | ||
|
||
|
||
/** | ||
* context_tracking_task_switch - context switch the syscall callbacks | ||
* @prev: the task that is being switched out | ||
* @next: the task that is being switched in | ||
* | ||
* The context tracking uses the syscall slow path to implement its user-kernel | ||
* boundaries probes on syscalls. This way it doesn't impact the syscall fast | ||
* path on CPUs that don't do context tracking. | ||
* | ||
* But we need to clear the flag on the previous task because it may later | ||
* migrate to some CPU that doesn't do the context tracking. As such the TIF | ||
* flag may not be desired there. | ||
*/ | ||
void context_tracking_task_switch(struct task_struct *prev, | ||
struct task_struct *next) | ||
{ | ||
|