我有一个 kmem_cache,其中包含受 RCU 保护的数据。
在更新受 RCU 保护的指针后,我还使用 call_rcu() 来进一步释放元素。
在销毁缓存之前,我必须确保所有的slab都被释放。
文档说synchronize_rcu()和call_rcu()的回调都会等待当前宽限期结束。因此,不能保证一旦synchronize_rcu()返回,所有call_rcu()的回调都完成,特别是释放slab。我也没有找到保证所有 call_rcu() 回调都以 FIFO 方式同步调用,这可以允许我们再使用一个 call_rcu() 来销毁缓存。
那么,我如何确保在销毁缓存之前释放所有受 RCU 保护的数据?
附注在我使用 x86-64 Linux 内核的情况下,synchronize_rcu() 会等待所有 call_rcu() 的回调完成并且它以某种方式工作,但就文档而言,这似乎是未定义的行为。
答案是 rcu_barrier() 函数。
来自 https://www.kernel.org/doc/Documentation/RCU/rcubarrier.txt:
卸载使用 call_rcu() 的模块
但是如果 p_callback 是在可卸载模块中定义的呢?
如果我们在某些 RCU 回调挂起时卸载模块,CPU 执行这些回调将会非常失望 它们后来被调用,如奇特地描述的 http://lwn.net/images/ns/kernel/rcu-drop.jpg。
我们可以尝试在模块退出代码路径中放置一个synchronize_rcu(), 但这还不够。尽管synchronize_rcu()确实等待 宽限期过去,它不会等待回调 完成。
人们可能会尝试几个连续的synchronize_rcu() 调用,但这仍然不能保证有效。如果有一个非常 RCU 回调负载很重,那么某些回调可能会被推迟 以便进行其他处理。这样的延期是 实时内核中需要以避免过度调度 延迟。
rcu_barrier()
我们需要 rcu_barrier() 原语。而不是等待 宽限期过后,rcu_barrier() 等待所有未完成的 RCU 回调来完成。请注意,rcu_barrier() 不会- 暗示synchronize_rcu(),特别是如果没有RCU回调 在任何地方排队,rcu_barrier() 都有权返回 立即,无需等待宽限期结束。
使用rcu_barrier()的伪代码如下:
- 防止发布任何新的 RCU 回调。
- 执行rcu_barrier()。
- 允许卸载模块。