The local_bh_disable
-function changes per-cpu (in case of x86 and recent kernels) __preempt_count
or current_thread_info()->preempt_count
otherwise.
Anyway that gives us a grace period, so we can assume that it will be redundant to do rcu_read_lock()
inside local_bh_disable()
. Indeed: in earlier kernels we can see that local_bh_disable()
is used for RCU and rcu_dereference()
is called subsequently inside e.g. the dev_queue_xmit
-function. Later local_bh_disable()
was replaced with rcu_read_lock_bh()
, which eventually became a little more complicated than just calling local_bh_disable()
. Now it looks like this:
static inline void rcu_read_lock_bh(void)
{
local_bh_disable();
__acquire(RCU_BH);
rcu_lock_acquire(&rcu_bh_lock_map);
RCU_LOCKDEP_WARN(!rcu_is_watching(),"rcu_read_lock_bh() used illegally while idle");
}
Also there are enough articles describing the RCU APIs. Here we can see:
Do you need to treat NMI handlers, hardirq handlers, and code segments with preemption disabled (whether via preempt_disable(), local_irq_save(), local_bh_disable(), or some other mechanism) as if they were explicit RCU readers? If so, RCU-sched is the only choice that will work for you.
This tells us to use the RCU Sched API in such cases, so rcu_dereference_sched()
should help. From this comprehensive table we can realise that rcu_dereference()
should be used only inside rcu_read_lock
/rcu_read_unlock
-markers.
However, it is not sufficiently clear. Can I use (in case of modern kernels) rcu_dereference()
inside local_bh_disable
/local_bh_enable
-markers without misgiving of anything going wrong?
P.S. In my case, I can't change the code calling local_bh_disable
to call e.g. rcu_read_lock_bh
, so my code is runnung with bh already disabled. Also the usual RCU API is used. Thus, it is fraught with rcu_read_lock
nested in local_bh_disable
.
You are not supposed to mix-and-match APIs. If you need to use the RCU-bh API, you need to use rcu_dereference_bh
.
You can see that if you called rcu_dereference_check
after rcu_read_lock_bh
, it would correctly surmise it is not called in a RCU read-side critical section; contrast the call to lock_is_held(&rcu_lock_map)
with rcu_lock_acquire(&rcu_bh_lock_map);
in your snippet above.
The kernel documentation for RCU here (search for the section on "rcu_dereference()") gives an explicit example of correct usage; rcu_dereference*
can only be called correctly after the corresponding rcu_read_lock*
function has completed.