Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rcu: Reverse rcu_dereference_check() conditions

The rcu_dereference_check() family of primitives evaluates the RCU
lockdep expression first, and only then evaluates the expression passed
in. This works fine normally, but can potentially fail in environments
(such as NMI handlers) where lockdep cannot be invoked. The problem is
that even if the expression passed in is "1", the compiler would need to
prove that the RCU lockdep expression (rcu_read_lock_held(), for example)
is free of side effects in order to be able to elide it. Given that
rcu_read_lock_held() is sometimes separately compiled, the compiler cannot
always use this optimization.

This commit therefore reverse the order of evaluation, so that the
expression passed in is evaluated first, and the RCU lockdep expression is
evaluated only if the passed-in expression evaluated to false, courtesy
of the C-language short-circuit boolean evaluation rules. This compells
the compiler to forego executing the RCU lockdep expression in cases
where the passed-in expression evaluates to "1" at compile time, so that
(for example) rcu_dereference_raw() can be guaranteed to execute safely
within an NMI handler.

Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>

+4 -4
+3 -3
include/linux/rcupdate.h
··· 729 729 * annotated as __rcu. 730 730 */ 731 731 #define rcu_dereference_check(p, c) \ 732 - __rcu_dereference_check((p), rcu_read_lock_held() || (c), __rcu) 732 + __rcu_dereference_check((p), (c) || rcu_read_lock_held(), __rcu) 733 733 734 734 /** 735 735 * rcu_dereference_bh_check() - rcu_dereference_bh with debug checking ··· 739 739 * This is the RCU-bh counterpart to rcu_dereference_check(). 740 740 */ 741 741 #define rcu_dereference_bh_check(p, c) \ 742 - __rcu_dereference_check((p), rcu_read_lock_bh_held() || (c), __rcu) 742 + __rcu_dereference_check((p), (c) || rcu_read_lock_bh_held(), __rcu) 743 743 744 744 /** 745 745 * rcu_dereference_sched_check() - rcu_dereference_sched with debug checking ··· 749 749 * This is the RCU-sched counterpart to rcu_dereference_check(). 750 750 */ 751 751 #define rcu_dereference_sched_check(p, c) \ 752 - __rcu_dereference_check((p), rcu_read_lock_sched_held() || (c), \ 752 + __rcu_dereference_check((p), (c) || rcu_read_lock_sched_held(), \ 753 753 __rcu) 754 754 755 755 #define rcu_dereference_raw(p) rcu_dereference_check(p, 1) /*@@@ needed? @@@*/
+1 -1
include/linux/srcu.h
··· 182 182 * lockdep_is_held() calls. 183 183 */ 184 184 #define srcu_dereference_check(p, sp, c) \ 185 - __rcu_dereference_check((p), srcu_read_lock_held(sp) || (c), __rcu) 185 + __rcu_dereference_check((p), (c) || srcu_read_lock_held(sp), __rcu) 186 186 187 187 /** 188 188 * srcu_dereference - fetch SRCU-protected pointer for later dereferencing