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。