aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2013-05-24 15:23:40 -0400
committerIngo Molnar <mingo@kernel.org>2013-06-19 06:55:10 -0400
commit29bb9e5a75684106a37593ad75ec75ff8312731b (patch)
treedb2e11c064e8648c874b1a73c226000661c86f33 /kernel
parent873b4c65b519fd769940eb281f77848227d4e5c1 (diff)
tracing/context-tracking: Add preempt_schedule_context() for tracing
Dave Jones hit the following bug report: =============================== [ INFO: suspicious RCU usage. ] 3.10.0-rc2+ #1 Not tainted ------------------------------- include/linux/rcupdate.h:771 rcu_read_lock() used illegally while idle! other info that might help us debug this: RCU used illegally from idle CPU! rcu_scheduler_active = 1, debug_locks = 0 RCU used illegally from extended quiescent state! 2 locks held by cc1/63645: #0: (&rq->lock){-.-.-.}, at: [<ffffffff816b39fd>] __schedule+0xed/0x9b0 #1: (rcu_read_lock){.+.+..}, at: [<ffffffff8109d645>] cpuacct_charge+0x5/0x1f0 CPU: 1 PID: 63645 Comm: cc1 Not tainted 3.10.0-rc2+ #1 [loadavg: 40.57 27.55 13.39 25/277 64369] Hardware name: Gigabyte Technology Co., Ltd. GA-MA78GM-S2H/GA-MA78GM-S2H, BIOS F12a 04/23/2010 0000000000000000 ffff88010f78fcf8 ffffffff816ae383 ffff88010f78fd28 ffffffff810b698d ffff88011c092548 000000000023d073 ffff88011c092500 0000000000000001 ffff88010f78fd60 ffffffff8109d7c5 ffffffff8109d645 Call Trace: [<ffffffff816ae383>] dump_stack+0x19/0x1b [<ffffffff810b698d>] lockdep_rcu_suspicious+0xfd/0x130 [<ffffffff8109d7c5>] cpuacct_charge+0x185/0x1f0 [<ffffffff8109d645>] ? cpuacct_charge+0x5/0x1f0 [<ffffffff8108dffc>] update_curr+0xec/0x240 [<ffffffff8108f528>] put_prev_task_fair+0x228/0x480 [<ffffffff816b3a71>] __schedule+0x161/0x9b0 [<ffffffff816b4721>] preempt_schedule+0x51/0x80 [<ffffffff816b4800>] ? __cond_resched_softirq+0x60/0x60 [<ffffffff816b6824>] ? retint_careful+0x12/0x2e [<ffffffff810ff3cc>] ftrace_ops_control_func+0x1dc/0x210 [<ffffffff816be280>] ftrace_call+0x5/0x2f [<ffffffff816b681d>] ? retint_careful+0xb/0x2e [<ffffffff816b4805>] ? schedule_user+0x5/0x70 [<ffffffff816b4805>] ? schedule_user+0x5/0x70 [<ffffffff816b6824>] ? retint_careful+0x12/0x2e ------------[ cut here ]------------ What happened was that the function tracer traced the schedule_user() code that tells RCU that the system is coming back from userspace, and to add the CPU back to the RCU monitoring. Because the function tracer does a preempt_disable/enable_notrace() calls the preempt_enable_notrace() checks the NEED_RESCHED flag. If it is set, then preempt_schedule() is called. But this is called before the user_exit() function can inform the kernel that the CPU is no longer in user mode and needs to be accounted for by RCU. The fix is to create a new preempt_schedule_context() that checks if the kernel is still in user mode and if so to switch it to kernel mode before calling schedule. It also switches back to user mode coming back from schedule in need be. The only user of this currently is the preempt_enable_notrace(), which is only used by the tracing subsystem. Signed-off-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/1369423420.6828.226.camel@gandalf.local.home Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/context_tracking.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c
index 65349f07b878..66677003e223 100644
--- a/kernel/context_tracking.c
+++ b/kernel/context_tracking.c
@@ -71,6 +71,46 @@ void user_enter(void)
71 local_irq_restore(flags); 71 local_irq_restore(flags);
72} 72}
73 73
74#ifdef CONFIG_PREEMPT
75/**
76 * preempt_schedule_context - preempt_schedule called by tracing
77 *
78 * The tracing infrastructure uses preempt_enable_notrace to prevent
79 * recursion and tracing preempt enabling caused by the tracing
80 * infrastructure itself. But as tracing can happen in areas coming
81 * from userspace or just about to enter userspace, a preempt enable
82 * can occur before user_exit() is called. This will cause the scheduler
83 * to be called when the system is still in usermode.
84 *
85 * To prevent this, the preempt_enable_notrace will use this function
86 * instead of preempt_schedule() to exit user context if needed before
87 * calling the scheduler.
88 */
89void __sched notrace preempt_schedule_context(void)
90{
91 struct thread_info *ti = current_thread_info();
92 enum ctx_state prev_ctx;
93
94 if (likely(ti->preempt_count || irqs_disabled()))
95 return;
96
97 /*
98 * Need to disable preemption in case user_exit() is traced
99 * and the tracer calls preempt_enable_notrace() causing
100 * an infinite recursion.
101 */
102 preempt_disable_notrace();
103 prev_ctx = exception_enter();
104 preempt_enable_no_resched_notrace();
105
106 preempt_schedule();
107
108 preempt_disable_notrace();
109 exception_exit(prev_ctx);
110 preempt_enable_notrace();
111}
112EXPORT_SYMBOL_GPL(preempt_schedule_context);
113#endif /* CONFIG_PREEMPT */
74 114
75/** 115/**
76 * user_exit - Inform the context tracking that the CPU is 116 * user_exit - Inform the context tracking that the CPU is