skb_unref() 的行为是否符合预期?

Does skb_unref() behave as it's meant to?

前言:

如果您熟悉用于管理网络数据包的 linux 内核缓冲区,即 sk_buff,那么您可能知道释放 skb:__kfree_skb()

根据我对 sk_buff 代码和 API 的理解,__kfree_skb() 是释放 sk_buff 的函数。它是一个内部辅助函数,理论上用户不应该调用它——用户应该调用它的包装函数之一,比如 kfree_skb()consume_skb()(或其他一些安全包装器)。
在调用 __kfree_skb() 之前,包装函数应该检查并减少 sk_buff 引用计数,如果它达到 0,包装函数可以调用 __kfree_skb() 来释放 sk_buff。一些包装器自己完成,其他的只是调用 skb_unref()(大约 3 年前添加到主线)。

希望你还在我身边。我说到点子上了。我最近注意到 skb_unref() 没有按照我的预期去做。直到最新版本的 linux 内核 v5.8,skb_unref() 看起来像这样:

/**
 * skb_unref - decrement the skb's reference count
 * @skb: buffer
 *
 * Returns true if we can free the skb.
 */
static inline bool skb_unref(struct sk_buff *skb)
{
    if (unlikely(!skb))
        return false;
    if (likely(refcount_read(&skb->users) == 1))
        smp_rmb();
    else if (likely(!refcount_dec_and_test(&skb->users)))
        return false;

    return true;
}

问题:

好吧,这个很久以前的explained in a comment(拼写错误LOL):

/*
 * If users==1, we are the only owner and are can avoid redundant
 * atomic change.
 */

长话短说:这只是一个微妙的优化。

如果引用计数为 1,这意味着您是该资源的唯一所有者,因此您可以安全地避免浪费时间以原子方式递减引用计数,并继续释放它。

if the last user that actually frees the skb doesn't bother to set the skb to NULL after calling kfree_skb(), then when some other user will check this skb refcount, it may accidentally try to free it again!

如果refcount是1,你是唯一的用户,还有谁会打电话给kfree_skb()?除了你,没有人。

I just tried to call kfree_skb() twice, and everything crashed

你还有什么期待? Linux 内核代码不是你的保姆,如果你 double-free 某些东西,你当然会导致崩溃。它可能会在死前打印一些警告,但仅此而已。基本上任何类型的分配都是如此。就像在任何其他 C 程序中一样,您必须确保只释放一次内存。


PS:kfree_skb() 的行为一直和你现在看到的一样,甚至在 v2.6 之前,唯一的区别是它的一些代码“最近”被移到了skb_unref()(在 v4.13 中)。