大冬の小站

银河麒麟v10适配Intel_I219-LM网卡驱动修复记录

2025/11/28
36
0

📋 背景说明

在将 Intel e1000e 驱动(版本 3.4.0.2)从旧内核移植到麒麟系统(内核 4.19.90)时,遇到了网卡无法识别的问题。本文详细记录了问题排查和解决的完整过程。

硬件环境

  • 网卡型号: Intel Ethernet Connection (17) I219-LM

  • 系统: Kylin Linux 4.19.90-89.27.v2401.ky10.x86_64

软件环境

  • 驱动版本: e1000e-3.4.0.2

  • 内核版本: 4.19.90

  • 编译器: gcc


🔍 问题发现

初始编译错误

执行 make 时遇到多个编译错误:

/root/下载/e1000e-3.4.0.2/src/netdev.c:6237:8: 错误:从不兼容的指针类型赋值
   frag = &skb_shinfo(skb)->frags[f];
​
/root/下载/e1000e-3.4.0.2/src/netdev.c:8344:2: 错误:implicit declaration of function 'init_timer'
  init_timer(&adapter->watchdog_timer);
​
/root/下载/e1000e-3.4.0.2/src/netdev.c:8346:25: 错误:'struct timer_list'没有名为'data'的成员
  adapter->watchdog_timer.data = (unsigned long)adapter;

网卡识别问题

修复编译错误后,驱动加载成功,但网卡未被识别:

$ lspci | grep -i ethernet
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (17) I219-LM (rev 11)
​
$ lsmod | grep e1000
e1000e                245760  0
​
$ ip link show
# 没有看到对应的网络接口(如eth0、enpXXX等)

关键问题:IRQ 255 - 这是无效的中断号,表明设备未被驱动正确初始化。

$ lspci -vnn -s 00:1f.6
00:1f.6 Ethernet controller [0200]: Intel Corporation Ethernet Connection (17) I219-LM [8086:1a1c] (rev 11)
    Flags: bus master, fast devsel, latency 0, IRQ 255  # ❌ 无效中断
    Memory at 80900000 (32-bit, non-prefetchable) [size=128K]

🔎 原因分析

问题 1: 内核 API 变更导致的编译错误

1.1 skb_frag 类型变化

问题根源:Linux 内核在较新版本中将 skb_frag_struct 替换为 skb_frag_t(即 struct bio_vec)。

错误代码(第 6235 行):

const struct skb_frag_struct *frag;  // ❌ 旧API
frag = &skb_shinfo(skb)->frags[f];
len = skb_frag_size(frag);

内核期望

const skb_frag_t *frag;  // ✅ 新API (bio_vec)

1.2 Timer API 变化

问题根源:内核 4.15+版本移除了旧的 timer API,不再支持 init_timer()timer.data 成员。

错误代码(第 8344-8350 行):

init_timer(&adapter->watchdog_timer);                    // ❌ 已废弃
adapter->watchdog_timer.function = e1000_watchdog;
adapter->watchdog_timer.data = (unsigned long)adapter;   // ❌ data成员已移除
​
init_timer(&adapter->phy_info_timer);
adapter->phy_info_timer.function = e1000_update_phy_info;
adapter->phy_info_timer.data = (unsigned long)adapter;

新 API 要求

  • 使用 timer_setup() 替代 init_timer()

  • 回调函数签名从 void callback(unsigned long data) 改为 void callback(struct timer_list *t)

  • 使用 from_timer() 宏获取容器结构

问题 2: 设备 ID 缺失

核心原因:e1000e 3.4.0.2 驱动发布于 2017 年,不支持 2021 年后发布的 Alder Lake 平台 I219-LM 网卡(设备 ID: 0x1a1c)。

验证方法

$ grep -i "1a1c" /home/dadong/下载/e1000e-3.4.0.2/src/*
# 无输出 - 证明驱动不支持该设备ID

驱动中现有的 I219 系列支持

// hw.h 中的定义
#define E1000_DEV_ID_PCH_SPT_I219_LM    0x156F  // Sunrise Point
#define E1000_DEV_ID_PCH_CNP_I219_LM6   0x15BD  // Cannon Point
#define E1000_DEV_ID_PCH_CNP_I219_LM7   0x15BB
// 缺少 0x1a1c (Alder Lake)

🛠️ 解决方案

步骤 1: 修复内核 API 兼容性问题

1.1 修复 skb_frag 类型

文件: src/netdev.c 第 6235 行

// 修改前
for (f = 0; f < nr_frags; f++) {
    const struct skb_frag_struct *frag;  // ❌
    frag = &skb_shinfo(skb)->frags[f];
    len = skb_frag_size(frag);
    // ...
}
​
// 修改后
for (f = 0; f < nr_frags; f++) {
    const skb_frag_t *frag;  // ✅ 使用新类型
    frag = &skb_shinfo(skb)->frags[f];
    len = skb_frag_size(frag);
    // ...
}

1.2 修复 Timer 回调函数

文件: src/netdev.c 第 5774 行和第 5389 行

修改 watchdog timer 回调

// 修改前
static void e1000_watchdog(unsigned long data)
{
    struct e1000_adapter *adapter = (struct e1000_adapter *)data;
    schedule_work(&adapter->watchdog_task);
}
​
// 修改后
static void e1000_watchdog(struct timer_list *t)
{
    struct e1000_adapter *adapter = from_timer(adapter, t, watchdog_timer);
    schedule_work(&adapter->watchdog_task);
}

修改 phy_info timer 回调

// 修改前
static void e1000_update_phy_info(unsigned long data)
{
    struct e1000_adapter *adapter = (struct e1000_adapter *)data;
    if (test_bit(__E1000_DOWN, &adapter->state))
        return;
    schedule_work(&adapter->update_phy_task);
}
​
// 修改后
static void e1000_update_phy_info(struct timer_list *t)
{
    struct e1000_adapter *adapter = from_timer(adapter, t, phy_info_timer);
    if (test_bit(__E1000_DOWN, &adapter->state))
        return;
    schedule_work(&adapter->update_phy_task);
}

1.3 修复 Timer 初始化

文件: src/netdev.c 第 8344-8350 行

// 修改前
init_timer(&adapter->watchdog_timer);
adapter->watchdog_timer.function = e1000_watchdog;
adapter->watchdog_timer.data = (unsigned long)adapter;
​
init_timer(&adapter->phy_info_timer);
adapter->phy_info_timer.function = e1000_update_phy_info;
adapter->phy_info_timer.data = (unsigned long)adapter;
​
// 修改后
timer_setup(&adapter->watchdog_timer, e1000_watchdog, 0);
timer_setup(&adapter->phy_info_timer, e1000_update_phy_info, 0);

关键宏说明

  • from_timer(var, callback_timer, timer_fieldname): 从 timer_list 指针获取包含它的结构体指针

  • timer_setup(timer, callback, flags): 初始化 timer 并设置回调函数

步骤 2: 添加 I219-LM 设备支持

2.1 添加设备 ID 宏定义

文件: src/hw.h 第 99-103 行之后

// 在现有I219定义后添加
#define E1000_DEV_ID_PCH_CNP_I219_LM6    0x15BD
#define E1000_DEV_ID_PCH_CNP_I219_V6     0x15BE
#define E1000_DEV_ID_PCH_CNP_I219_LM7    0x15BB
#define E1000_DEV_ID_PCH_CNP_I219_V7     0x15BC
#define E1000_DEV_ID_PCH_ADP_I219_LM17   0x1a1c  /* Alder Lake PCH */  // ✅ 新增

命名规则

  • PCH_ADP: Alder Lake PCH 平台代号

  • I219_LM17: I219 系列的 LM(LAN Manager)版本 17

  • 0x1a1c: 实际的 PCI 设备 ID

2.2 注册 PCI 设备

文件: src/netdev.c 第 8662-8665 行之后

static const struct pci_device_id e1000_pci_tbl[] = {
    // ... 现有设备 ...
​
    { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM6), board_pch_cnp },
    { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V6), board_pch_cnp },
    { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM7), board_pch_cnp },
    { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V7), board_pch_cnp },
    { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM17), board_pch_cnp },  // ✅ 新增
​
    { 0, 0, 0, 0, 0, 0, 0 }  /* terminate list */
};

说明

  • PCI_VDEVICE(INTEL, device_id): 自动填充 Intel 厂商 ID (0x8086)

  • board_pch_cnp: 使用 Cannon Point 的 board 配置(与 I219-LM7 相同架构)


🔧 编译与安装

完整操作流程

# 1. 清理旧编译文件
cd e1000e-3.4.0.2/src
make clean
​
# 2. 重新编译
make
​
# 3. 安装新驱动(会替换系统驱动)
sudo make install
​
# 4. 卸载旧驱动
sudo rmmod e1000e
​
# 5. 加载新驱动
sudo modprobe e1000e
​
# 6. 查看加载日志
dmesg | tail -30
​
# 7. 验证网卡识别
ip link show
lspci -k -s 00:1f.6

预期输出

成功的 dmesg 日志

[ 3456.789012] e1000e: Intel(R) PRO/1000 Network Driver - 3.4.0.2-NAPI
[ 3456.789015] e1000e: Copyright(c) 1999 - 2017 Intel Corporation.
[ 3456.789234] e1000e 0000:00:1f.6: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 3456.890123] e1000e 0000:00:1f.6 0000:00:1f.6 (uninitialized): registered PHC clock
[ 3456.991234] e1000e 0000:00:1f.6 eth0: (PCI Express:2.5GT/s:Width x1) 00:11:22:33:44:55
[ 3456.991237] e1000e 0000:00:1f.6 eth0: Intel(R) PRO/1000 Network Connection
[ 3456.991345] e1000e 0000:00:1f.6 eth0: MAC: 12, PHY: 12, PBA No: FFFFFF-0FF
[ 3456.992456] e1000e 0000:00:1f.6 enp0s31f6: renamed from eth0

网卡正常识别

$ ip link show
...
4: enp0s31f6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
    link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
​
$ lspci -k -s 00:1f.6
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (17) I219-LM (rev 11)
    Subsystem: Lenovo Device 1064
    Kernel driver in use: e1000e  ✅
    Kernel modules: e1000e

持久化配置

为防止系统更新覆盖驱动,建议更新 initramfs:

# Kylin/RedHat系列
sudo dracut --force
​
# Ubuntu/Debian系列
sudo update-initramfs -u

📝 附录:完整修改 diff

--- a/src/hw.h
+++ b/src/hw.h
@@ -100,6 +100,7 @@
 #define E1000_DEV_ID_PCH_CNP_I219_V6        0x15BE
 #define E1000_DEV_ID_PCH_CNP_I219_LM7       0x15BB
 #define E1000_DEV_ID_PCH_CNP_I219_V7        0x15BC
+#define E1000_DEV_ID_PCH_ADP_I219_LM17      0x1a1c  /* Alder Lake PCH */
​
 #define E1000_REVISION_4    4
​
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -5386,9 +5386,9 @@
  * Need to wait a few seconds after link up to get diagnostic information from
  * the phy
  **/
-static void e1000_update_phy_info(unsigned long data)
+static void e1000_update_phy_info(struct timer_list *t)
 {
-    struct e1000_adapter *adapter = (struct e1000_adapter *)data;
+    struct e1000_adapter *adapter = from_timer(adapter, t, phy_info_timer);
​
     if (test_bit(__E1000_DOWN, &adapter->state))
         return;
@@ -5771,9 +5771,9 @@
  * e1000_watchdog - Timer Call-back
  * @data: pointer to adapter cast into an unsigned long
  **/
-static void e1000_watchdog(unsigned long data)
+static void e1000_watchdog(struct timer_list *t)
 {
-    struct e1000_adapter *adapter = (struct e1000_adapter *)data;
+    struct e1000_adapter *adapter = from_timer(adapter, t, watchdog_timer);
​
     /* Do the rest outside of interrupt context */
     schedule_work(&adapter->watchdog_task);
@@ -6234,7 +6234,7 @@
     }
​
     for (f = 0; f < nr_frags; f++) {
-        const struct skb_frag_struct *frag;
+        const skb_frag_t *frag;
​
         frag = &skb_shinfo(skb)->frags[f];
         len = skb_frag_size(frag);
@@ -8341,13 +8341,8 @@
         goto err_eeprom;
     }
​
-    init_timer(&adapter->watchdog_timer);
-    adapter->watchdog_timer.function = e1000_watchdog;
-    adapter->watchdog_timer.data = (unsigned long)adapter;
-
-    init_timer(&adapter->phy_info_timer);
-    adapter->phy_info_timer.function = e1000_update_phy_info;
-    adapter->phy_info_timer.data = (unsigned long)adapter;
+    timer_setup(&adapter->watchdog_timer, e1000_watchdog, 0);
+    timer_setup(&adapter->phy_info_timer, e1000_update_phy_info, 0);
​
     INIT_WORK(&adapter->reset_task, e1000_reset_task);
     INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);
@@ -8663,6 +8663,7 @@
     { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V6), board_pch_cnp },
     { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM7), board_pch_cnp },
     { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V7), board_pch_cnp },
+    { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ADP_I219_LM17), board_pch_cnp },
​
     { 0, 0, 0, 0, 0, 0, 0 }  /* terminate list */
 };

下载地址