perfbook 读书笔记:SLAB_DESTROY_BY_RCU
perfbook 书中第8.3.3.6节讲到了类型安全,提到了Linux内核中的 SLAB_DESTROY_BY_RCU。但是书中并没有更详细的介绍这个特性。这里更详细地说一下。
要理解 SLABDESTROYBY_RCU,最重要的是看 kmem_cache_destroy(),以 slub 为例,
[c]
void kmem_cache_destroy(struct kmem_cache *s)
{
down_write(&slub_lock);
s->refcount—;
if (!s->refcount) {
list_del(&s->list);
up_write(&slub_lock);
if (kmem_cache_close(s)) {
printk(KERN_ERR “SLUB %s: %s called for cache that “
“still has objects.n”, s->name, __func);
dump_stack();
}
if (s->flags & SLAB_DESTROY_BY_RCU)
rcu_barrier();
sysfs_slab_remove(s);
} else
up_write(&slub_lock);
}
[/c]
看那两行就足够了,rcu_barrier(); 是用来等待所有的 call_rcu() 回调函数结束,那么,kmem_cache_destroy() 在 SLAB_DESTROY_BY_RCU 的情况很明显就是等待所有 kmem_cache_free() 完成。
和普通的用 kmem_cache_alloc() 分配出来的对象相比,这种内存分配方式提供了更弱的保证,普通的分配可以保证对象不会被释放回 cache 中,而这个仅仅保证它不会被彻底释放,但不保证它会被放回 cache 重新利用,也就是说类型是不变的,即所谓的类型安全。实际上,在此期间它很有可能已经被放回 cache 重新利用了。
正是因为这种保证更弱了,所以在 rcu_read_lock() 并发区内就要多一个检查,检查是否还是之前的那个对象,因为这是类型安全的,所以对它进行同类型的检查是完全合法的。一个很好的例子是 __lock_task_sighand():
[c]
struct sighand_struct __lock_task_sighand(struct task_struct tsk,
unsigned long flags)
{
struct sighand_struct sighand;
for (;;) {
local_irq_save(*flags);
rcu_read_lock();
sighand = rcu_dereference(tsk->sighand);
if (unlikely(sighand == NULL)) {
rcu_read_unlock();
local_irq_restore(*flags);
break;
}
spin_lock(&sighand->siglock);
if (likely(sighand == tsk->sighand)) {
rcu_read_unlock();
break;
}
spin_unlock(&sighand->siglock);
rcu_read_unlock();
local_irq_restore(*flags);
}
return sighand;
}
[/c]
注意,->siglock 是在 ->ctor() 中初始化的,所以刚分配出来的 sighand 的 ->siglock 也是已初始化的。
在此基础上,Linux 内核中还衍生出一个新的哈希链表,hlist_nulls,具体可以参考 Documentation/RCU/rculist_nulls.txt。