From: Frederic Weisbecker Date: Thu, 23 Jun 2011 23:12:38 +0000 (+0200) Subject: rcu: Detect illegal rcu dereference in extended quiescent state X-Git-Tag: next-20110914~19^2~6 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=607694e497a8a3c0d189d6e2838e6d7e4f839255;p=karo-tx-linux.git rcu: Detect illegal rcu dereference in extended quiescent state Report that none of the rcu read lock maps are held while in an RCU extended quiescent state (in this case, the RCU extended quiescent state is dyntick-idle mode). This helps detect any use of rcu_dereference() and friends from within dyntick-idle mode. Uses of RCU from within dyntick-idle mode are totally ignored by RCU, hence the importance of these checks. Signed-off-by: Frederic Weisbecker Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Lai Jiangshan Signed-off-by: Paul E. McKenney --- diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 8d7efc8a062f..7d8fa7cd32bc 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -231,6 +231,14 @@ static inline void destroy_rcu_head_on_stack(struct rcu_head *head) } #endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ + +#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_NO_HZ) +extern bool rcu_check_extended_qs(void); +#else +static inline bool rcu_check_extended_qs(void) { return false; } +#endif + + #ifdef CONFIG_DEBUG_LOCK_ALLOC #define PROVE_RCU(a) a @@ -264,11 +272,25 @@ extern int debug_lockdep_rcu_enabled(void); * * Checks debug_lockdep_rcu_enabled() to prevent false positives during boot * and while lockdep is disabled. + * + * Note that if the CPU is in an extended quiescent state, for example, + * if the CPU is in dyntick-idle mode, then rcu_read_lock_held() returns + * false even if the CPU did an rcu_read_lock(). The reason for this is + * that RCU ignores CPUs that are in extended quiescent states, so such + * a CPU is effectively never in an RCU read-side critical section + * regardless of what RCU primitives it invokes. This state of affairs + * is required -- RCU would otherwise need to periodically wake up + * dyntick-idle CPUs, which would defeat the whole purpose of dyntick-idle + * mode. */ static inline int rcu_read_lock_held(void) { if (!debug_lockdep_rcu_enabled()) return 1; + + if (rcu_check_extended_qs()) + return 0; + return lock_is_held(&rcu_lock_map); } @@ -292,6 +314,16 @@ extern int rcu_read_lock_bh_held(void); * * Check debug_lockdep_rcu_enabled() to prevent false positives during boot * and while lockdep is disabled. + * + * Note that if the CPU is in an extended quiescent state, for example, + * if the CPU is in dyntick-idle mode, then rcu_read_lock_held() returns + * false even if the CPU did an rcu_read_lock(). The reason for this is + * that RCU ignores CPUs that are in extended quiescent states, so such + * a CPU is effectively never in an RCU read-side critical section + * regardless of what RCU primitives it invokes. This state of affairs + * is required -- RCU would otherwise need to periodically wake up + * dyntick-idle CPUs, which would defeat the whole purpose of dyntick-idle + * mode. */ #ifdef CONFIG_PREEMPT static inline int rcu_read_lock_sched_held(void) @@ -300,6 +332,10 @@ static inline int rcu_read_lock_sched_held(void) if (!debug_lockdep_rcu_enabled()) return 1; + + if (rcu_check_extended_qs()) + return 0; + if (debug_locks) lockdep_opinion = lock_is_held(&rcu_sched_lock_map); return lockdep_opinion || preempt_count() != 0 || irqs_disabled(); diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 5031cafb6f8a..e4d8a986c095 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -87,12 +87,27 @@ EXPORT_SYMBOL_GPL(debug_lockdep_rcu_enabled); * that require that they be called within an RCU read-side critical * section. * - * Check debug_lockdep_rcu_enabled() to prevent false positives during boot. + * Check debug_lockdep_rcu_enabled() to prevent false positives during boot + * and while lockdep is disabled. + * + * Note that if the CPU is in an extended quiescent state, for example, + * if the CPU is in dyntick-idle mode, then rcu_read_lock_held() returns + * false even if the CPU did an rcu_read_lock(). The reason for this is + * that RCU ignores CPUs that are in extended quiescent states, so such + * a CPU is effectively never in an RCU read-side critical section + * regardless of what RCU primitives it invokes. This state of affairs + * is required -- RCU would otherwise need to periodically wake up + * dyntick-idle CPUs, which would defeat the whole purpose of dyntick-idle + * mode. */ int rcu_read_lock_bh_held(void) { if (!debug_lockdep_rcu_enabled()) return 1; + + if (rcu_check_extended_qs()) + return 0; + return in_softirq() || irqs_disabled(); } EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index da775c87f27f..9e493b9f615f 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -78,6 +78,20 @@ void rcu_exit_nohz(void) rcu_dynticks_nesting++; } + +#ifdef CONFIG_PROVE_RCU + +bool rcu_check_extended_qs(void) +{ + if (!rcu_dynticks_nesting) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(rcu_check_extended_qs); + +#endif + #endif /* #ifdef CONFIG_NO_HZ */ /* diff --git a/kernel/rcutree.c b/kernel/rcutree.c index f0a94324032b..c9b4adfc3e36 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -465,6 +465,22 @@ void rcu_irq_exit(void) rcu_enter_nohz(); } +#ifdef CONFIG_PROVE_RCU + +bool rcu_check_extended_qs(void) +{ + struct rcu_dynticks *rdtp; + + rdtp = &per_cpu(rcu_dynticks, raw_smp_processor_id()); + if (atomic_read(&rdtp->dynticks) & 0x1) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(rcu_check_extended_qs); + +#endif /* CONFIG_PROVE_RCU */ + #ifdef CONFIG_SMP /*