如何保护 RCU reader 部分不被抢占?

How RCU reader section is protected from preemption?

(来自 LWN 上的一篇文章)

 1 rcu_read_lock();
 2 list_for_each_entry_rcu(p, head, list) {
 3   do_something_with(p->a, p->b, p->c);
 4 }
 5 rcu_read_unlock();

RCU 更新操作将执行 synchronize_rcu() 以断言每个 CPU 已切换上下文,因此每个 RCU-reader 已完成其工作。但是RCU必须依赖reader不被抢占。事实上,LWN 接下来说:

Although this simple approach works for kernels in which preemption is disabled across RCU read-side critical sections, in other words, for non-CONFIG_PREEMPT and CONFIG_PREEMPT kernels, it does not work for CONFIG_PREEMPT_RT realtime (-rt) kernels.

我知道 non-CONFIG_PREEMPT 内核禁用抢占,但为什么 CONFIG_PREEMPT 内核也可以?

我们需要 CONFIG_PREEMPT 内核的 RCU。如果没有抢占或阻塞,那么我们就不会陷入这种同步混乱之中。有两种类型的 RCU 实现:

1) Non-preemptible RCU implementation
2) Preemptible RCU implementation

当在一个 CPU 上调用 synchronize_rcu() 而在其他 CPU 上调用时 在 RCU 读端临界区内,则 synchronize_rcu() 保证阻塞直到所有其他 CPUs 退出了它们的临界区。同样,如果 call_rcu() 被调用 在一个 CPU 上,而其他 CPU 在 RCU 读端临界范围内 部分,延迟调用相应的 RCU 回调 直到所有其他 CPU 退出它们的临界区。

在不可抢占的 RCU 实现中,在 RCU 读端临界区中阻塞是非法的。 CONFIG_PREEMPT 中的抢占式 RCU 实现 (PREEMPT_RCU) 内核构建,RCU 读端临界区可能被抢占, 但明确阻止是非法的。最后,在抢占式 RCU 中 实时实现(使用 -rt 补丁集)内核构建,RCU 读端临界区可能被抢占,它们也可能阻塞,但是 仅在获取受优先级继承约束的自旋锁时。

在 CONFIG_PREEMPT 内核上是可以的,因为在任务被抢占之前注意完成 rcu 读取临界区。调度程序检查当前任务是否在 rcu 读取临界区中,如果是,它会提高其优先级以完成临界区。有关详细信息,请参阅本文:http://lwn.net/Articles/220677/