aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2012-11-27 13:33:25 -0500
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2012-11-30 14:40:07 -0500
commit91d1aa43d30505b0b825db8898ffc80a8eca96c7 (patch)
tree911636f846d800c8a44efd540842dc726ec7c191 /kernel
parent4e79752c25ec221ac1e28f8875b539ed7631a0db (diff)
context_tracking: New context tracking susbsystem
Create a new subsystem that probes on kernel boundaries to keep track of the transitions between level contexts with two basic initial contexts: user or kernel. This is an abstraction of some RCU code that use such tracking to implement its userspace extended quiescent state. We need to pull this up from RCU into this new level of indirection because this tracking is also going to be used to implement an "on demand" generic virtual cputime accounting. A necessary step to shutdown the tick while still accounting the cputime. Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Li Zhong <zhong@linux.vnet.ibm.com> Cc: Gilad Ben-Yossef <gilad@benyossef.com> Reviewed-by: Steven Rostedt <rostedt@goodmis.org> [ paulmck: fix whitespace error and email address. ] Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/context_tracking.c83
-rw-r--r--kernel/rcutree.c64
-rw-r--r--kernel/sched/core.c11
4 files changed, 92 insertions, 67 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index 0dfeca4324ee..f90bbfc9727f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -110,6 +110,7 @@ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o
110obj-$(CONFIG_PADATA) += padata.o 110obj-$(CONFIG_PADATA) += padata.o
111obj-$(CONFIG_CRASH_DUMP) += crash_dump.o 111obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
112obj-$(CONFIG_JUMP_LABEL) += jump_label.o 112obj-$(CONFIG_JUMP_LABEL) += jump_label.o
113obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o
113 114
114$(obj)/configs.o: $(obj)/config_data.h 115$(obj)/configs.o: $(obj)/config_data.h
115 116
diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c
new file mode 100644
index 000000000000..e0e07fd55508
--- /dev/null
+++ b/kernel/context_tracking.c
@@ -0,0 +1,83 @@
1#include <linux/context_tracking.h>
2#include <linux/rcupdate.h>
3#include <linux/sched.h>
4#include <linux/percpu.h>
5#include <linux/hardirq.h>
6
7struct context_tracking {
8 /*
9 * When active is false, hooks are not set to
10 * minimize overhead: TIF flags are cleared
11 * and calls to user_enter/exit are ignored. This
12 * may be further optimized using static keys.
13 */
14 bool active;
15 enum {
16 IN_KERNEL = 0,
17 IN_USER,
18 } state;
19};
20
21static DEFINE_PER_CPU(struct context_tracking, context_tracking) = {
22#ifdef CONFIG_CONTEXT_TRACKING_FORCE
23 .active = true,
24#endif
25};
26
27void user_enter(void)
28{
29 unsigned long flags;
30
31 /*
32 * Some contexts may involve an exception occuring in an irq,
33 * leading to that nesting:
34 * rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
35 * This would mess up the dyntick_nesting count though. And rcu_irq_*()
36 * helpers are enough to protect RCU uses inside the exception. So
37 * just return immediately if we detect we are in an IRQ.
38 */
39 if (in_interrupt())
40 return;
41
42 WARN_ON_ONCE(!current->mm);
43
44 local_irq_save(flags);
45 if (__this_cpu_read(context_tracking.active) &&
46 __this_cpu_read(context_tracking.state) != IN_USER) {
47 __this_cpu_write(context_tracking.state, IN_USER);
48 rcu_user_enter();
49 }
50 local_irq_restore(flags);
51}
52
53void user_exit(void)
54{
55 unsigned long flags;
56
57 /*
58 * Some contexts may involve an exception occuring in an irq,
59 * leading to that nesting:
60 * rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
61 * This would mess up the dyntick_nesting count though. And rcu_irq_*()
62 * helpers are enough to protect RCU uses inside the exception. So
63 * just return immediately if we detect we are in an IRQ.
64 */
65 if (in_interrupt())
66 return;
67
68 local_irq_save(flags);
69 if (__this_cpu_read(context_tracking.state) == IN_USER) {
70 __this_cpu_write(context_tracking.state, IN_KERNEL);
71 rcu_user_exit();
72 }
73 local_irq_restore(flags);
74}
75
76void context_tracking_task_switch(struct task_struct *prev,
77 struct task_struct *next)
78{
79 if (__this_cpu_read(context_tracking.active)) {
80 clear_tsk_thread_flag(prev, TIF_NOHZ);
81 set_tsk_thread_flag(next, TIF_NOHZ);
82 }
83}
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index 7733eb56e156..e441b77b614e 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -207,9 +207,6 @@ EXPORT_SYMBOL_GPL(rcu_note_context_switch);
207DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { 207DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = {
208 .dynticks_nesting = DYNTICK_TASK_EXIT_IDLE, 208 .dynticks_nesting = DYNTICK_TASK_EXIT_IDLE,
209 .dynticks = ATOMIC_INIT(1), 209 .dynticks = ATOMIC_INIT(1),
210#if defined(CONFIG_RCU_USER_QS) && !defined(CONFIG_RCU_USER_QS_FORCE)
211 .ignore_user_qs = true,
212#endif
213}; 210};
214 211
215static long blimit = 10; /* Maximum callbacks per rcu_do_batch. */ 212static long blimit = 10; /* Maximum callbacks per rcu_do_batch. */
@@ -420,29 +417,7 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter);
420 */ 417 */
421void rcu_user_enter(void) 418void rcu_user_enter(void)
422{ 419{
423 unsigned long flags; 420 rcu_eqs_enter(1);
424 struct rcu_dynticks *rdtp;
425
426 /*
427 * Some contexts may involve an exception occuring in an irq,
428 * leading to that nesting:
429 * rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
430 * This would mess up the dyntick_nesting count though. And rcu_irq_*()
431 * helpers are enough to protect RCU uses inside the exception. So
432 * just return immediately if we detect we are in an IRQ.
433 */
434 if (in_interrupt())
435 return;
436
437 WARN_ON_ONCE(!current->mm);
438
439 local_irq_save(flags);
440 rdtp = &__get_cpu_var(rcu_dynticks);
441 if (!rdtp->ignore_user_qs && !rdtp->in_user) {
442 rdtp->in_user = true;
443 rcu_eqs_enter(true);
444 }
445 local_irq_restore(flags);
446} 421}
447 422
448/** 423/**
@@ -579,27 +554,7 @@ EXPORT_SYMBOL_GPL(rcu_idle_exit);
579 */ 554 */
580void rcu_user_exit(void) 555void rcu_user_exit(void)
581{ 556{
582 unsigned long flags; 557 rcu_eqs_exit(1);
583 struct rcu_dynticks *rdtp;
584
585 /*
586 * Some contexts may involve an exception occuring in an irq,
587 * leading to that nesting:
588 * rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
589 * This would mess up the dyntick_nesting count though. And rcu_irq_*()
590 * helpers are enough to protect RCU uses inside the exception. So
591 * just return immediately if we detect we are in an IRQ.
592 */
593 if (in_interrupt())
594 return;
595
596 local_irq_save(flags);
597 rdtp = &__get_cpu_var(rcu_dynticks);
598 if (rdtp->in_user) {
599 rdtp->in_user = false;
600 rcu_eqs_exit(true);
601 }
602 local_irq_restore(flags);
603} 558}
604 559
605/** 560/**
@@ -722,21 +677,6 @@ int rcu_is_cpu_idle(void)
722} 677}
723EXPORT_SYMBOL(rcu_is_cpu_idle); 678EXPORT_SYMBOL(rcu_is_cpu_idle);
724 679
725#ifdef CONFIG_RCU_USER_QS
726void rcu_user_hooks_switch(struct task_struct *prev,
727 struct task_struct *next)
728{
729 struct rcu_dynticks *rdtp;
730
731 /* Interrupts are disabled in context switch */
732 rdtp = &__get_cpu_var(rcu_dynticks);
733 if (!rdtp->ignore_user_qs) {
734 clear_tsk_thread_flag(prev, TIF_NOHZ);
735 set_tsk_thread_flag(next, TIF_NOHZ);
736 }
737}
738#endif /* #ifdef CONFIG_RCU_USER_QS */
739
740#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) 680#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU)
741 681
742/* 682/*
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 36f260864f65..80f80dfca70e 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -72,6 +72,7 @@
72#include <linux/slab.h> 72#include <linux/slab.h>
73#include <linux/init_task.h> 73#include <linux/init_task.h>
74#include <linux/binfmts.h> 74#include <linux/binfmts.h>
75#include <linux/context_tracking.h>
75 76
76#include <asm/switch_to.h> 77#include <asm/switch_to.h>
77#include <asm/tlb.h> 78#include <asm/tlb.h>
@@ -1886,8 +1887,8 @@ context_switch(struct rq *rq, struct task_struct *prev,
1886 spin_release(&rq->lock.dep_map, 1, _THIS_IP_); 1887 spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
1887#endif 1888#endif
1888 1889
1890 context_tracking_task_switch(prev, next);
1889 /* Here we just switch the register state and the stack. */ 1891 /* Here we just switch the register state and the stack. */
1890 rcu_user_hooks_switch(prev, next);
1891 switch_to(prev, next, prev); 1892 switch_to(prev, next, prev);
1892 1893
1893 barrier(); 1894 barrier();
@@ -2911,7 +2912,7 @@ asmlinkage void __sched schedule(void)
2911} 2912}
2912EXPORT_SYMBOL(schedule); 2913EXPORT_SYMBOL(schedule);
2913 2914
2914#ifdef CONFIG_RCU_USER_QS 2915#ifdef CONFIG_CONTEXT_TRACKING
2915asmlinkage void __sched schedule_user(void) 2916asmlinkage void __sched schedule_user(void)
2916{ 2917{
2917 /* 2918 /*
@@ -2920,9 +2921,9 @@ asmlinkage void __sched schedule_user(void)
2920 * we haven't yet exited the RCU idle mode. Do it here manually until 2921 * we haven't yet exited the RCU idle mode. Do it here manually until
2921 * we find a better solution. 2922 * we find a better solution.
2922 */ 2923 */
2923 rcu_user_exit(); 2924 user_exit();
2924 schedule(); 2925 schedule();
2925 rcu_user_enter(); 2926 user_enter();
2926} 2927}
2927#endif 2928#endif
2928 2929
@@ -3027,7 +3028,7 @@ asmlinkage void __sched preempt_schedule_irq(void)
3027 /* Catch callers which need to be fixed */ 3028 /* Catch callers which need to be fixed */
3028 BUG_ON(ti->preempt_count || !irqs_disabled()); 3029 BUG_ON(ti->preempt_count || !irqs_disabled());
3029 3030
3030 rcu_user_exit(); 3031 user_exit();
3031 do { 3032 do {
3032 add_preempt_count(PREEMPT_ACTIVE); 3033 add_preempt_count(PREEMPT_ACTIVE);
3033 local_irq_enable(); 3034 local_irq_enable();