Softnet 驱动程序问题

探测指南

地址验证

应验证从设备获得的任何硬件层地址。 例如,对于以太网,请使用 linux/etherdevice.h 检查:is_valid_ether_addr()

关闭/停止指南

静止

调用 ndo_stop 例程后,硬件不得接收或发送任何数据。 所有正在传输的数据包都必须中止。 如果需要,轮询或等待任何重置命令完成。

自动关闭

如果设备仍处于 UP 状态,unregister_netdevice 将调用 ndo_stop 例程。

传输路径指南

提前停止队列

在任何正常情况下,ndo_start_xmit 方法都不得返回 NETDEV_TX_BUSY。 除非您的设备无法提前知道其传输功能何时会变忙,否则这被认为是硬错误。

相反,它必须正确维护队列。 例如,对于实现 scatter-gather 的驱动程序,这意味着

static u32 drv_tx_avail(struct drv_ring *dr)
{
        u32 used = READ_ONCE(dr->prod) - READ_ONCE(dr->cons);

        return dr->tx_ring_size - (used & bp->tx_ring_mask);
}

static netdev_tx_t drv_hard_start_xmit(struct sk_buff *skb,
                                       struct net_device *dev)
{
        struct drv *dp = netdev_priv(dev);
        struct netdev_queue *txq;
        struct drv_ring *dr;
        int idx;

        idx = skb_get_queue_mapping(skb);
        dr = dp->tx_rings[idx];
        txq = netdev_get_tx_queue(dev, idx);

        //...
        /* This should be a very rare race - log it. */
        if (drv_tx_avail(dr) <= skb_shinfo(skb)->nr_frags + 1) {
                netif_stop_queue(dev);
                netdev_warn(dev, "Tx Ring full when queue awake!\n");
                return NETDEV_TX_BUSY;
        }

        //... queue packet to card ...

        netdev_tx_sent_queue(txq, skb->len);

        //... update tx producer index using WRITE_ONCE() ...

        if (!netif_txq_maybe_stop(txq, drv_tx_avail(dr),
                                  MAX_SKB_FRAGS + 1, 2 * MAX_SKB_FRAGS))
                dr->stats.stopped++;

        //...
        return NETDEV_TX_OK;
}

然后在您的 TX 回收事件处理结束时

//... update tx consumer index using WRITE_ONCE() ...

netif_txq_completed_wake(txq, cmpl_pkts, cmpl_bytes,
                         drv_tx_avail(dr), 2 * MAX_SKB_FRAGS);

无锁队列停止/唤醒辅助宏

netif_txq_maybe_stop() 和 __netif_txq_completed_wake() 宏旨在安全地实现停止和唤醒 netdev 队列,而无需完全锁定保护。

我们假设不会有并发的停止尝试,也不会有并发的唤醒尝试。 try-stop 应该从 xmit 处理程序发生,而唤醒应该从 NAPI 轮询上下文触发。 这两者可以并发运行(单生产者,单消费者)。

try-stop 侧预计从 xmit 处理程序运行,因此它不会重新安排 Tx(netif_tx_start_queue() 代替 netif_tx_wake_queue())。 在 xmit 处理程序之外使用 stop 宏可能会导致启用 xmit 队列但未运行。 唤醒侧没有类似的环境限制。

这些宏保证如果有可用空间,环将不会保持停止状态,但它们不能阻止环已满时的错误唤醒! 驱动程序应在 xmit 处理程序启动时检查环是否已满。

在调用宏之前,必须更新所有描述符环索引(和其他相关的共享状态)。

无独占所有权

ndo_start_xmit 方法不得修改克隆 SKB 的共享部分。

及时完成

不要忘记,一旦您从 ndo_start_xmit 方法返回 NETDEV_TX_OK,您的驱动程序就有责任在有限的时间内释放 SKB。

例如,这意味着如果未发送新的 TX 数据包,您的 TX 缓解方案不允许 TX 数据包“挂起”在 TX 环中,永远无法回收。 如果没有释放发送缓冲区空间,此错误可能会导致套接字死锁。

如果您从 ndo_start_xmit 方法返回 NETDEV_TX_BUSY,则不得保留对该 SKB 的任何引用,也不得尝试释放它。