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 的任何引用,也不得嘗試釋放它。