网络设备驱动故障处理案例
案例 1:网卡注册成功但无法 ping 通
排查步骤:
bash
# 1. 检查接口状态
ip link show eth0
ip addr show eth0
# 2. 手动 UP 接口
ip link set eth0 up
# 3. 检查链路状态
ethtool eth0 | grep "Link detected"
# 4. 抓包验证
tcpdump -i eth0 -n
# 5. 查看驱动统计
ethtool -S eth0
cat /proc/net/dev常见原因:
netif_carrier_off()未在链路 UP 时调用netif_carrier_on()- MAC 地址全零或广播地址
eth_type_trans()未调用,skb->protocol未设置
案例 2:接收丢包
现象:ethtool -S eth0 显示 rx_dropped 持续增加。
排查:
bash
# 查看 RX 环形缓冲区大小
ethtool -g eth0
# 增大 RX 缓冲区
ethtool -G eth0 rx 4096
# 查看软中断统计
cat /proc/net/softnet_stat
# 第二列:dropped(softirq 处理不过来)
# 第三列:time_squeeze(NAPI budget 用完)解决方法:
c
/* 增大 NAPI budget(默认 64) */
/* 注意:budget 总和不能超过 netdev_budget(默认 300) */
/* 增大 RX 描述符环 */
#define RX_RING_SIZE 512 /* 增大到 512 或 1024 */
/* 使用 GRO 减少协议栈处理次数 */
ndev->features |= NETIF_F_GRO;案例 3:发送卡死(TX hang)
现象:ping 延迟突然变大,netstat 显示发送队列积压。
排查:
bash
# 查看发送队列长度
ip link show eth0 | grep qlen
# 查看 TX 统计
ethtool -S eth0 | grep tx
# 检查是否触发 watchdog
dmesg | grep "NETDEV WATCHDOG"常见原因:
netif_stop_queue()后未调用netif_wake_queue()- 发送完成中断未触发(中断丢失)
- TX 描述符环满且未回收
解决方法:
c
/* 实现 ndo_tx_timeout(watchdog 超时回调) */
static void my_tx_timeout(struct net_device *ndev, unsigned int txqueue)
{
struct mynet_priv *priv = netdev_priv(ndev);
netdev_warn(ndev, "TX timeout, resetting...\n");
ndev->stats.tx_errors++;
/* 重置硬件 */
reset_tx_ring(priv);
netif_wake_queue(ndev);
}
static const struct net_device_ops my_ops = {
.ndo_tx_timeout = my_tx_timeout,
};
/* 设置 watchdog 超时时间 */
ndev->watchdog_timeo = 5 * HZ; /* 5 秒 */案例 4:DMA 映射错误
现象:dmesg 出现 DMA-API: device driver failed to check map error。
解决方法:
c
/* ✅ 始终检查 DMA 映射结果 */
dma_addr_t dma_addr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr)) {
dev_kfree_skb(skb);
ndev->stats.tx_dropped++;
return NETDEV_TX_OK;
}案例 5:多队列网卡性能不均衡
现象:top 显示只有一个 CPU 处理网络中断。
解决方法:
bash
# 查看中断分配
cat /proc/interrupts | grep eth0
# 手动设置 IRQ 亲和性
echo 0f > /proc/irq/42/smp_affinity # 绑定到 CPU 0-3
# 或使用 irqbalance 自动均衡
systemctl start irqbalance
# 开启 RSS(Receive Side Scaling)
ethtool -X eth0 equal 4 # 4 个队列均衡驱动侧需要声明多队列支持:
c
/* 分配多队列 net_device */
ndev = alloc_etherdev_mqs(sizeof(*priv), tx_queues, rx_queues);
/* 设置 RSS 哈希类型 */
ndev->hw_features |= NETIF_F_RXHASH;