[PATCH] rcu: Debug nasty rcu user mode API recursion

From: Frederic Weisbecker
Date: Sat Sep 29 2012 - 08:16:09 EST


Add some debug code to chase down the origin of the fault.

Not-Signed-off-by: Frederic Weisbecker <fweisbec@xxxxxxxxx>
---
arch/x86/mm/fault.c | 1 +
include/linux/rcupdate.h | 1 +
kernel/rcutree.c | 32 ++++++++++++++++++++++++++++++++
kernel/rcutree.h | 1 +
4 files changed, 35 insertions(+)

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index a530b23..a5f0eb5 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -1232,6 +1232,7 @@ good_area:
dotraplinkage void __kprobes
do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
+ rcu_check_user_recursion(regs);
exception_enter(regs);
__do_page_fault(regs, error_code);
exception_exit(regs);
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 7c968e4..14ba908 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -199,6 +199,7 @@ extern void rcu_user_enter_after_irq(void);
extern void rcu_user_exit_after_irq(void);
extern void rcu_user_hooks_switch(struct task_struct *prev,
struct task_struct *next);
+extern void rcu_check_user_recursion(struct pt_regs *regs);
#else
static inline void rcu_user_enter(void) { }
static inline void rcu_user_exit(void) { }
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index 4fb2376..63b84f5 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -405,6 +405,20 @@ void rcu_idle_enter(void)
EXPORT_SYMBOL_GPL(rcu_idle_enter);

#ifdef CONFIG_RCU_USER_QS
+void rcu_check_user_recursion(struct pt_regs *regs)
+{
+ unsigned long flags;
+ static int printed;
+
+ local_irq_save(flags);
+ if (__this_cpu_read(rcu_dynticks.recursion) && !printed) {
+ printed = 1;
+ printk("Found recursion\n");
+ show_regs(regs);
+ }
+ local_irq_restore(flags);
+}
+
/**
* rcu_user_enter - inform RCU that we are resuming userspace.
*
@@ -433,10 +447,20 @@ void rcu_user_enter(void)

local_irq_save(flags);
rdtp = &__get_cpu_var(rcu_dynticks);
+ if (WARN_ON_ONCE(rdtp->recursion)) {
+ local_irq_restore(flags);
+ return;
+ }
+
+ rdtp->recursion = true;
+ barrier();
+
if (!rdtp->ignore_user_qs && !rdtp->in_user) {
rdtp->in_user = true;
rcu_eqs_enter(true);
}
+ rdtp->recursion = false;
+
local_irq_restore(flags);
}

@@ -590,10 +614,18 @@ void rcu_user_exit(void)

local_irq_save(flags);
rdtp = &__get_cpu_var(rcu_dynticks);
+ if (WARN_ON_ONCE(rdtp->recursion)) {
+ local_irq_restore(flags);
+ return;
+ }
+
+ rdtp->recursion = true;
+ barrier();
if (rdtp->in_user) {
rdtp->in_user = false;
rcu_eqs_exit(true);
}
+ rdtp->recursion = false;
local_irq_restore(flags);
}

diff --git a/kernel/rcutree.h b/kernel/rcutree.h
index 5faf05d..1bde9d5 100644
--- a/kernel/rcutree.h
+++ b/kernel/rcutree.h
@@ -103,6 +103,7 @@ struct rcu_dynticks {
int tick_nohz_enabled_snap; /* Previously seen value from sysfs. */
#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
#ifdef CONFIG_RCU_USER_QS
+ bool recursion;
bool ignore_user_qs; /* Treat userspace as extended QS or not */
bool in_user; /* Is the CPU in userland from RCU POV? */
#endif
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/