在将 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]问题根源: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)问题根源:内核 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() 宏获取容器结构
核心原因: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)文件: 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);
// ...
}文件: 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);
}文件: 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 并设置回调函数
文件: 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
文件: 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--- 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 */
};