Skip to content

网络设备驱动故障处理案例

案例 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;

褚成志的笔记