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
相关产品推荐
相关产品推荐

