【问题标题】:Does skb_unref() behave as it's meant to?skb_unref() 的行为是否符合预期?
【发布时间】:2021-01-27 01:50:18
【问题描述】:

前言:

如果您熟悉用于管理网络数据包的 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 kernel 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;
}

问题:

  • 阅读代码后,我注意到如果refcount_read 返回1,我们不会将skb->users 减为0,但函数确实返回true
  • 这意味着,如果refcount_read 返回 1,skb_unref() 告诉kfree_skb()(或其他使用skb_unref() 的包装函数)它可以释放skb(但它不会减少@987654344 @ 到 0),如果最后一个真正释放 skb 的用户在调用 kfree_skb() 之后没有费心将 skb 设置为 NULL,那么当其他一些用户将检查这个 skb 引用计数时,它可能不小心再次尝试释放它!
  • 我还确保我没有看错代码,我只是尝试调用kfree_skb() 两次,然后一切都崩溃了......
  • 我错了吗?也许我不明白如何正确使用kfree_skb()/skb_unref()?我只是想知道,如果 kfree_skb()/skb_unref() 没有按应有的方式行事,那到底是怎么回事。

【问题讨论】:

    标签: c linux linux-kernel network-programming linux-device-driver


    【解决方案1】:

    嗯,这很久以前的explained in a comment(拼写错误哈哈):

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

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

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

    如果最后一个真正释放 skb 的用户在调用 kfree_skb() 后没有费心将 NULL 设置为再次释放它!

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

    我刚刚尝试拨打kfree_skb() 两次,一切都崩溃了

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


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

    【讨论】:

    • 我猜最新内核版本的评论误导了我:“删除对缓冲区的引用并在使用计数为零时释放它”。但我必须说,经过您的解释,我认为我对 SKB 机制的理解要好得多。 10倍!!!所以根据我的理解,如果我分配SKB,那么我一定是调用kfree_skb()的那个,如果我想把它传递给不同的层,那么这个层必须调用skb_get()(或者复制它)如果它还不想让我发布它(当这个层用 SKB 完成时,它也应该调用 kfree_skb())。
    • @J.M.是的,我会说你的理解是正确的
    猜你喜欢
    • 1970-01-01
    • 2017-01-27
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    • 2021-04-09
    相关资源
    最近更新 更多