在 local_bh_disable()/local_bh_enable() 中使用 rcu_dereference() 安全吗?
Is it safe to use rcu_dereference() inside local_bh_disable()/local_bh_enable()?
local_bh_disable
-函数改变每个-cpu(在 x86 和最新内核的情况下)__preempt_count
或 current_thread_info()->preempt_count
否则。
无论如何,这给了我们一个宽限期,因此我们可以假设在 local_bh_disable()
内执行 rcu_read_lock()
是多余的。确实:in earlier kernels we can see local_bh_disable()
用于 RCU 并且 rcu_dereference()
随后在内部调用,例如dev_queue_xmit
-函数。后来 local_bh_disable()
被 rcu_read_lock_bh()
取代,最终变得比仅仅调用 local_bh_disable()
复杂一点。现在看起来像这样:
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");
}
也有足够的文章描述 RCU APIs。 Here我们可以看到:
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.
这告诉我们在这种情况下使用 RCU Sched API,因此 rcu_dereference_sched()
应该有所帮助。从 this comprehensive table 我们可以意识到 rcu_dereference()
应该只在 rcu_read_lock
/rcu_read_unlock
-markers.
中使用
但是,还不够清楚。我可以在 local_bh_disable
/local_bh_enable
标记中使用(在现代内核的情况下)rcu_dereference()
而不担心出现任何问题吗?
P.S。就我而言,我无法将调用 local_bh_disable
的代码更改为调用例如rcu_read_lock_bh
,所以我的代码运行时 bh 已经被禁用。还使用了通常的 RCU API。因此,它充满了嵌套在 local_bh_disable
.
中的 rcu_read_lock
您不应该混搭 API。如果需要使用RCU-bh API,则需要使用rcu_dereference_bh
.
您可以看到,如果您在 rcu_read_lock_bh
之后调用 rcu_dereference_check
,它会正确地推测它是 not called in a RCU read-side critical section;将上面代码段中对 lock_is_held(&rcu_lock_map)
的调用与 rcu_lock_acquire(&rcu_bh_lock_map);
进行对比。
RCU here 的内核文档(搜索 "rcu_dereference()" 部分)给出了正确用法的明确示例; rcu_dereference*
只有对应的rcu_read_lock*
函数完成后才能正确调用。
local_bh_disable
-函数改变每个-cpu(在 x86 和最新内核的情况下)__preempt_count
或 current_thread_info()->preempt_count
否则。
无论如何,这给了我们一个宽限期,因此我们可以假设在 local_bh_disable()
内执行 rcu_read_lock()
是多余的。确实:in earlier kernels we can see local_bh_disable()
用于 RCU 并且 rcu_dereference()
随后在内部调用,例如dev_queue_xmit
-函数。后来 local_bh_disable()
被 rcu_read_lock_bh()
取代,最终变得比仅仅调用 local_bh_disable()
复杂一点。现在看起来像这样:
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");
}
也有足够的文章描述 RCU APIs。 Here我们可以看到:
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.
这告诉我们在这种情况下使用 RCU Sched API,因此 rcu_dereference_sched()
应该有所帮助。从 this comprehensive table 我们可以意识到 rcu_dereference()
应该只在 rcu_read_lock
/rcu_read_unlock
-markers.
但是,还不够清楚。我可以在 local_bh_disable
/local_bh_enable
标记中使用(在现代内核的情况下)rcu_dereference()
而不担心出现任何问题吗?
P.S。就我而言,我无法将调用 local_bh_disable
的代码更改为调用例如rcu_read_lock_bh
,所以我的代码运行时 bh 已经被禁用。还使用了通常的 RCU API。因此,它充满了嵌套在 local_bh_disable
.
rcu_read_lock
您不应该混搭 API。如果需要使用RCU-bh API,则需要使用rcu_dereference_bh
.
您可以看到,如果您在 rcu_read_lock_bh
之后调用 rcu_dereference_check
,它会正确地推测它是 not called in a RCU read-side critical section;将上面代码段中对 lock_is_held(&rcu_lock_map)
的调用与 rcu_lock_acquire(&rcu_bh_lock_map);
进行对比。
RCU here 的内核文档(搜索 "rcu_dereference()" 部分)给出了正确用法的明确示例; rcu_dereference*
只有对应的rcu_read_lock*
函数完成后才能正确调用。