You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核4.15.3下,如何在ndo_start_xmit中获取sk_buff_head?

解决Linux 4.15.3内核中ndo_start_xmit访问sk_buff_head的问题

嘿,针对你在修改虚拟网卡驱动、实现依赖帧顺序的分片功能时遇到的问题,我来分享几个在4.15.3内核下可行的方案:

方案1:利用网卡的默认TX队列

虚拟网络设备的TX队列本身就是sk_buff_head结构,你可以通过net_device对象直接获取对应的TX队列,进而拿到队列的锁和链表头:

int ndo_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    // 根据skb的队列映射获取对应的TX队列
    struct netdev_queue *txq = netdev_get_tx_queue(dev, skb->queue_mapping);
    struct sk_buff_head *tx_head = &txq->skbs;

    // 获取队列锁(注意用bh锁,避免软中断上下文死锁)
    spin_lock_bh(&txq->lock);

    // 这里可以安全操作tx_head队列,比如添加分片、调整帧顺序等
    // 示例:将skb加入队列尾部
    // skb_queue_tail(tx_head, skb);

    spin_unlock_bh(&txq->lock);

    // 根据你的逻辑处理返回值
    return NETDEV_TX_OK;
}

如果你的虚拟设备只有一个TX队列,也可以直接用dev->tx_queue来获取,不用通过queue_mapping。

方案2:自定义私有队列(更灵活)

如果你的分片逻辑需要独立于内核默认TX队列的缓存机制,比如需要攒够一定数量的帧再分片发送,那可以在设备的私有数据里自定义sk_buff_head和对应的锁:

首先在设备初始化时创建私有队列:

static int my_netdev_init(struct net_device *dev)
{
    struct my_priv *priv = netdev_priv(dev);

    // 初始化自定义TX队列和锁
    skb_queue_head_init(&priv->custom_tx_queue);
    spin_lock_init(&priv->custom_tx_lock);

    return 0;
}

然后在ndo_start_xmit中直接访问私有队列:

int ndo_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct my_priv *priv = netdev_priv(dev);

    spin_lock_bh(&priv->custom_tx_lock);

    // 操作自定义队列,实现你的分片逻辑
    // 比如:缓存skb,直到满足分片条件再处理
    // skb_queue_tail(&priv->custom_tx_queue, skb);

    spin_unlock_bh(&priv->custom_tx_lock);

    return NETDEV_TX_OK;
}

关键注意事项

  • 内核早已移除了skb直接指向sk_buff_head的链接,所以必须通过队列对象来访问链表头,这是内核设计上的变化,目的是避免滥用链表结构。
  • 在操作队列时一定要用对应的自旋锁保护,并且在软中断上下文(ndo_start_xmit通常运行在这个上下文)中建议使用spin_lock_bh/spin_unlock_bh,防止软中断抢占导致死锁。
  • 操作sk_buff队列时请使用内核提供的标准函数(如skb_queue_tail、skb_dequeue、skb_peek等),不要直接修改链表节点的prev/next指针,避免破坏队列结构。

内容的提问来源于stack exchange,提问作者Eknoes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:50:10