所有上層協議(IP、ARP 等)最終都呼叫 dev_queue_xmit(struct sk_buff *skb) 將封裝好幀交給驅動。關鍵職責:
netif_needs_gso)__skb_linearize)skb_checksum_help)dev_pick_tx,多隊列網卡)
__dev_xmit_skb()
├─ 若 Qdisc 可繞過(TCQ_F_CAN_BYPASS)且隊列空 → sch_direct_xmit() 直發
└─ 否則 → qdisc_enqueue_root() 入隊 → qdisc_run() 排程發送
dev_kfree_skb_irq 放入 completion_queue)| 函數 | 核心動作 |
|---|---|
| qdisc_run() | 設定 __QDISC_STATE_RUNNING,呼叫 __qdisc_run |
| __qdisc_run() | 循環 qdisc_restart,需讓 CPU 或時間片用盡則 __netif_schedule 重排 |
| qdisc_restart() | dequeue_skb 取幀 → sch_direct_xmit 發送 |
| sch_direct_xmit() | 釋放 qdisc lock → 取 netif_tx_lock → dev_hard_start_xmit |
| dev_hard_start_xmit() | 呼叫驅動 ndo_start_xmit,更新隊列時間戳 |
qdisc_lock(q):保護 Qdisc 內部 skb 隊列__QDISC_STATE_RUNNING:位元鎖,確保單 CPU 執行 Qdiscnetif_tx_lock (txq->_xmit_lock):保護驅動發送函數獨佔執行**規則**:持有 qdisc_lock 時不可持有 netif_tx_lock,反之亦然,sch_direct_xmit 中先釋放 qdisc_lock 再取 netif_tx_lock。
static netdev_tx_t loopback_xmit(struct sk_buff *skb, struct net_device *dev) {
skb_orphan(skb);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb); // 直接注入接收路徑,無真實發送
return NETDEV_TX_OK;
}
dev_queue_xmit_nit() 將克隆的 skb 傳給 ptype_all 鏈表上的 ETH_P_ALL 協議處理器,實現 tcpdump/Wireshark 抓包。
上層協議
│
▼
dev_queue_xmit() ── GSO/線性化/校驗和
│
▼
dev_pick_tx() 選隊列
│
▼
Qdisc (pfifo_fq / mq / noqueue)
│
├─ 可繞過且空 ──▶ sch_direct_xmit() ──▶ 驅動 ndo_start_xmit()
│
└─ 入隊 ──▶ qdisc_run() ──▶ 軟中斷 net_tx_action ──▶ qdisc_restart() 循環發送
掌握二層發送路徑、Qdisc 與軟中斷分工、三把鎖協作,是排查網卡驅動效能、丟包、延遲抖動的關鍵基石。