☰
银河麒麟V10离线编译e1000e与r8125网卡驱动及DKMS固化
2026/11/6 6:44:33 网站建设 项目流程

简介:面向银河麒麟V10桌面、服务器与工控终端的有线网卡适配,这份源码包汇集Intel e1000e与Realtek RTL8125两款千兆网卡驱动实现,适合国产化项目中的系统运维、整机集成和Linux驱动开发人员,用于解决网卡无法识别、驱动需重编译或内核接口不匹配等问题。包内共56个文件、约483KB,以21个头文件、19个C源码文件和3个Makefile脚本为主体,另含README说明、spec打包描述、shell脚本、补丁更新与校验文件,目录按驱动模块划分,便于查阅源码结构和编译配置。源码已针对银河麒麟V10内核处理重复定义冲突并调整部分函数参数,保留了两套独立驱动目录及构建入口,可减少从零移植时的反复试错。已有2750人学习,可作为复现编译、定位头文件依赖、排查模块加载与参数兼容问题的对照基线,也适合作为国产平台网卡驱动适配的参考样例。

1. 装完系统敲 ifconfig 只剩一个 lo:这堆驱动到底解决什么问题

有台内网机器,装完银河麒麟 V10,桌面起来一切正常,插上网线的灯不亮,ip a里只有一个 lo。lspci | grep -i ethernet却明明白白列出两块卡:一块 Intel 千兆,一块 Realtek 2.5G。硬件没问题,是系统自带内核里没把驱动编进去——RTL8125 这种 2.5G 芯片在 4.19、5.4 这类内核基线里压根没有,e1000e 又是老版本。更难受的是:装驱动要联网,联网要驱动,死循环。

这份资源就是能在银河麒麟 V10 上直接编译通过的 e1000e 和 rtl8125 网卡驱动源码。适合手上有一台不能联网的机器、必须自己把 ko 编出来再塞进去的人;也适合做整机适配、需要把驱动打进出厂镜像的工程师。

2. 编译环境准备:把 uname -r 和 /lib/modules 对齐

在动手之前得先认清一件事:网卡驱动不是普通的用户态程序,它必须和当前运行内核的vermagic严格对上,差一个字符都加载不进去。所以整条链路上第一重要的不是源码,是内核版本。

2.1 先确认你到底在哪个内核上编

很多人翻车就翻在这一步。系统盘上装了kernel-4.19.90-xx,但当前跑的可能不是它,或者你装的是kernel-devel,版本号尾部的 build 号差了一位,/lib/modules/$(uname -r)/build这个软链就指向一个不存在的目录,后面make一律报No such file or directory。

先把三件事查清楚:

# 1. 当前运行的内核版本,注意尾部的 build 号 uname -r # 2. 头文件目录是否存在,软链指向哪里 ls -l /lib/modules/$(uname -r)/build ls -l /lib/modules/$(uname -r)/source # 3. 已装的内核包和 devel 包是否版本一致 rpm -qa | grep -E '^kernel(-devel|-headers)?-'

uname -r打印出来的完整字符串就是内核的KERNELRELEASE,它由版本号、补丁号、构建号和发行版后缀拼成,驱动.ko加载时内核会拿这个串做校验。/lib/modules/$(uname -r)/build必须是一个有效软链,通常指向/usr/src/kernels/$(uname -r)。如果这个目录是空的或者软链断了,说明kernel-devel没装或者装错了版本——注意rpm -qa里 devel 包的版本必须和uname -r完全一致,不能只对到主版本号。

我一般的习惯是先把uname -r的输出复制下来,后面所有命令都拿这个串去拼,不靠手打:

export KVER=$(uname -r) echo $KVER ls /usr/src/kernels/$KVER >/dev/null && echo "devel ok" || echo "缺 kernel-devel"

这里用 shell 变量存住版本号,是因为后面编译命令里要反复出现/lib/modules/$(uname -r)/build。手敲两次以上基本一定会打错一位,尤其是那串带连字符的构建号。ls /usr/src/kernels/$KVER这个判断比看软链更直接,因为软链存在但目标目录为空的情况并不少见。

提示:如果uname -r和rpm -qa | grep kernel-devel的输出对不上,不要试图用软链接强行指过去,内核模块构建过程会读include/generated/utsrelease.h和.config,版本不对时编出来的模块虽然能make成功,但insmod会直接报Invalid module format。

2.2 工具链和 dkms 一次装齐

驱动编译除了 gcc、make,还要几个容易被忽略的包。elfutils-libelf-devel是给模块签名和 BTF 生成用的,缺了会在编译最后一步报错;dkms是后面做自动重编的关键;ethtool、pciutils、kmod是验证和加载时用的工具。

离线机器上的做法是先在一台能联网、系统版本相同的机器上把 rpm 依赖下载全,再拷过去:

# 在能联网的同版本机器上下载依赖包,不安装,只下到本地目录 mkdir -p /tmp/drv-rpms sudo dnf download --resolve --destdir=/tmp/drv-rpms \ gcc make elfutils-libelf-devel \ kernel-devel-$(uname -r) kernel-headers-$(uname -r) \ dkms ethtool pciutils kmod # 打包后拷到目标机器上安装 sudo rpm -ivh /tmp/drv-rpms/*.rpm --nodeps

--resolve会递归把依赖树一起下下来,这一步省不掉的。kernel-devel-$(uname -r)里带上版本号,是为了保证拿到的是当前内核的头文件,而不是仓库里最新的那个——仓库里的kernel-devel往往比你现在跑的内核新一两个小版本,装上去以后软链会指向新版本目录,编译出来的模块跟运行内核不匹配,属于典型的静默失败。

gcc 版本也值得留意。内核本身是用某个特定版本 gcc 编出来的,如果你用更高版本的 gcc 编模块,绝大多数情况没问题,但偶尔会遇到内核源码里的老代码触发新版编译器的告警升级,直接变成 error。真碰上了,加KCFLAGS="-Wno-error"通常能绕过去,但更好的做法是装一个和内核构建版本接近的 gcc。

2.3 两种源码形态:内核树内模块和厂商独立包

e1000e 和 r8125 这两个驱动的源码组织方式完全不同,编译命令也不一样,混淆了就会卡很久。

e1000e 是内核主线里的驱动,源码位置在drivers/net/ethernet/intel/e1000e/,目录里自带的 Makefile 是给内核整体构建系统用的,内容是obj-$(CONFIG_E1000E) += e1000e.o这种形式。你单独在这个目录里跑make -C /lib/modules/... M=$PWD modules会直接报错,因为CONFIG_E1000E在内核外部构建时根本没有定义,obj-$(CONFIG_E1000E)展开成obj-和空,等于什么都没编译。

r8125 则是厂商提供的独立源码包,结构是src/加一个顶层 Makefile,天生就是为了外部构建准备的,make一下就能出r8125.ko。

# 判断一个驱动源码目录属于哪种形态,看 Makefile 前几行就够了 head -20 drivers/net/ethernet/intel/e1000e/Makefile # 内核树内形态:obj-$(CONFIG_E1000E) += e1000e.o head -20 r8125-9.007.01/Makefile # 独立包形态:all: modules ; modules: $(MAKE) -C src/ modules

这两条head是排查时的第一手信息。如果 Makefile 里出现obj-$(CONFIG_XXX),说明它依赖内核的 Kconfig 状态,必须改成obj-m才能外部构建;如果出现all:、modules:这类目标,说明它已经是独立包的写法,直接make即可。这个判断能帮你在五分钟内确定接下来的路径,不用翻 README。

注意:e1000e 的源码版本必须和内核版本一致或者兼容,从别的版本内核里扒一份 e1000e 出来编译,常见的失败是struct net_device成员不存在、netif_napi_add参数数量对不上这一类编译错误,根子都是内核 API 变了。

3. e1000e 编译链路:从 Makefile 改造到 insmod 验证

e1000e 覆盖的是 Intel 的 PCIe 千兆系列,82574L、82583V、I219-V 这些常见型号都在里面。这套代码在主线里已经维护了十几年,代码量不小,但结构很清晰,拆开看主要就是硬件抽象层加网络设备层两部分。

3.1 e1000e 的源码结构

进到目录先看文件列表,能大致判断出各模块职责:

ls drivers/net/ethernet/intel/e1000e/ # 82571.c ich8lan.c mac.c manage.c nvm.c phy.c # ptp.c netdev.c ethtool.c param.c hw.h e1000e_hw.h Makefile

82571.c和ich8lan.c是两个硬件家族的适配层,前者对应 82571/82572/82573/82574/82583 这些独立 PCIe 网卡芯片,后者对应 ICH8 之后集成在南桥或 PCH 里的千兆 MAC。mac.c放的是 MAC 层通用寄存器操作,phy.c是 PHY 芯片的读写,nvm.c负责从网卡的 NVM(相当于网卡自己的小 EEPROM)里读 MAC 地址和配置,manage.c处理带外管理相关的逻辑。

真正和 Linux 网络子系统打交道的是netdev.c。它负责pci_driver的注册、net_device的申请和初始化、ndo_start_xmit发送回调、NAPI 轮询、中断处理,基本上一块网卡从 probe 到 up 到发第一个包,全在这个文件里。ethtool.c是对外的查询和设置接口,param.c定义了模块参数,ptp.c是 1588 精确时间协议支持。

这个分层方式值得借鉴:硬件差异被压在82571.c、ich8lan.c里,上层netdev.c只调用统一的hw->mac.ops.xxx函数指针,加一款新芯片基本只需要实现一组 ops。

3.2 Makefile 里真正起作用的三行

内核树内构建和外部构建的差别,全在这几行上。目录里原始内容是:

# 原始内容,只在内核树内构建时生效 obj-$(CONFIG_E1000E) += e1000e.o e1000e-objs := 82571.o ich8lan.o mac.o manage.o \ nvm.o phy.o ptp.o netdev.o ethtool.o param.o

第一行是条件追加,CONFIG_E1000E为y或m时才把e1000e.o加进构建目标。外部构建时这个变量是空的,所以必须改。改法有两种,我更推荐第一种:

# 改法一:在文件顶部加一行覆盖,保留原有内容便于对照 obj-m := e1000e.o obj-$(CONFIG_E1000E) += e1000e.o # 原行保留,外部构建时为空不影响 e1000e-objs := 82571.o ich8lan.o mac.o manage.o \ nvm.o phy.o ptp.o netdev.o ethtool.o param.o

obj-m是 kbuild 外部构建时的标准变量,值为模块名,kbuild 会把它编成e1000e.ko。e1000e-objs是复合目标拆解,告诉 kbuild 这个模块由哪几个.o链接而成,注意这里用的是模块名-objs的形式,模块名里不能有连字符,有的话要写成e1000e-y。

还有一处容易漏:ptp.o依赖内核的CONFIG_PTP_1588_CLOCK。如果目标内核没开这个选项,编译ptp.o时会报一堆未定义符号。这种情况下把ptp.o从e1000e-objs里去掉,同时在netdev.c里把 PTP 相关的函数调用用#ifdef CONFIG_E1000E_HWTS包起来——新版本内核里这个功能的开关叫CONFIG_E1000E_HWTS。只是为了上网的话,去掉 PTP 完全不影响功能。

3.3 编译、安装、加载三步走

Makefile 改好后,编译命令就一条,但参数要理解清楚:

# -C 切到内核构建目录,M= 告诉 kbuild 外部模块源码在哪,modules 是目标 make -C /lib/modules/$(uname -r)/build M=$PWD modules -j$(nproc) # 成功后当前目录下会出现 e1000e.ko ls -lh e1000e.ko modinfo e1000e.ko | head -8

-C /lib/modules/$(uname -r)/build是让 make 先cd到内核构建目录,读那里的顶层 Makefile,这样才能拿到完整的 kbuild 规则和编译参数(包括-I头文件路径、-D__KERNEL__、各种-mcmodel之类的架构参数)。M=$PWD是告诉 kbuild 待编译的外部模块在哪个目录。这两个参数缺一不可,只给M=不给-C=会去读当前目录的 Makefile,只给-C=不给M=会去编整个内核。

-j$(nproc)是并行编译,e1000e 文件不多,收益有限但没坏处。编完用modinfo检查一下,重点看vermagic这一行,它必须和uname -r对得上:

modinfo e1000e.ko | grep vermagic # vermagic: 4.19.90-xx.xx.xx.el8.x86_64 SMP mod_unload modversions

如果 vermagic 里的版本和当前内核不一致,说明-C指向的构建目录版本不对,回到 2.1 重新对齐。

加载分三步,顺序不能乱:

# 1. 拷到内核模块目录,注意路径要和 depmod 扫描的范围一致 sudo cp e1000e.ko /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/intel/e1000e/ # 2. 重建模块依赖索引,不执行这步 modprobe 找不到新模块 sudo depmod -a $(uname -r) # 3. 先卸载系统自带的同名模块(如果已加载),再加载新的 sudo modprobe -r e1000e 2>/dev/null sudo modprobe e1000e

depmod -a会扫描/lib/modules/$(uname -r)/下所有.ko,把模块名、别名、依赖关系写进modules.dep、modules.alias这些文件,modprobe靠这些索引定位模块。直接insmod是可以绕过索引的,但insmod不处理依赖,网卡驱动依赖的ptp、pps_core这些辅助模块不会被自动加载,所以正规做法还是走modprobe。

加载完立刻看dmesg,这是最直接的成功信号:

sudo dmesg | tail -20 | grep -i e1000e # 期望看到:[ 12.345678] e1000e 0000:03:00.0: NIC Link is Up 1000 Mbps Full Duplex ip -br link show

3.4 常用模块参数与调优

e1000e 支持几个运行时参数,调试和优化时用得上,写在/etc/modprobe.d/e1000e.conf里:

参数默认值说明
InterruptThrottleRate3中断节流,0 关闭,数值越大聚合越明显
SmartPowerDownEnable0空闲时降功耗,某些老 PHY 上会掉链路
EEE-1节能以太网,-1 跟随硬件,0 强制关
S0ixEnabled0现代待机支持,老平台开了容易没法唤醒

InterruptThrottleRate是这几个里最值得调的。默认值 3 是自适应模式,小包多的时候中断频繁,CPU 占用会上去;如果跑的是转发或者小包压力测试场景,把它压到固定值能让吞吐更稳,代价是延迟升高。SmartPowerDownEnable和EEE这两个在弱电环境、老交换机上属于玄学配置项,链路时不时掉一下又自己恢复,八成是它们导致的,先关掉再排查。

4. r8125 编译链路:2.5G 网卡与 r8169 的替换关系

RTL8125 是 Realtek 的 2.5G 有线网卡,设备 ID 是10ec:8125,现在很多主板板载都是这个。问题在于主线内核里负责 Realtek 有线的r8169驱动在很长一段时间里对 8125 只有基础支持,跑不满 2.5G,也不支持一些高级特性,所以厂商单独提供了r8125驱动包。

4.1 厂商源码包的目录结构与编译入口

解开压缩包后结构是这样的:

tar -xjf r8125-9.007.01.tar.bz2 cd r8125-9.007.01 ls # Makefile dkms.conf README src/ autorescan.sh ls src/ # r8125_n.c r8125_rss.c r8125_ethtool.c r8125_dash.c # r8125_firmware.c rtl_eeprom.c Makefile ...

顶层 Makefile 是个薄壳,真正的编译规则在src/Makefile里:

# 顶层 Makefile 的核心内容 all: modules modules: $(MAKE) -C src/ modules clean: $(MAKE) -C src/ clean

src/Makefile里定义了obj-m := r8125.o和r8125-objs := r8125_n.o r8125_rss.o r8125_ethtool.o r8125_dash.o。r8125_n.c是主体,网络设备注册、收发路径都在里面;r8125_rss.c是多队列接收侧扩展;r8125_ethtool.c是 ethtool 回调;r8125_dash.c是带外管理。

编译直接跑:

# 直接编译,默认使用当前运行内核的头文件 make -j$(nproc) # 如果报头文件找不到,显式指定内核目录 make -C /lib/modules/$(uname -r)/build M=$PWD/src modules -j$(nproc) # 产物 ls -lh src/r8125.ko

第一条命令走的是顶层 Makefile,它会自动去/lib/modules/$(uname -r)/build拿内核路径。第二条是手动指定,在交叉编译或者路径不规范时用。注意M=$PWD/src这里指向的是src目录而不是包根目录,因为 Makefile 在src里。

编译过程中如果出现error: 'netif_napi_add' 参数过多或者implicit declaration of function 'xxx',基本都是内核版本差异导致的 API 变化。Kylin V10 常见的 4.19 和 5.4 两条基线,厂商包一般都能直接过,真要碰到 5.15 以上内核,得手工改签名,这块在避坑章展开。

4.2 用 dkms 装,把内核升级的坑提前埋掉

r8125 有个麻烦之处:系统自带的r8169也会认这块卡,两个驱动抢同一个 PCI 设备。kernel 升级之后如果你只装了.ko文件,新内核启动时r8169又把它抓回去了,网速掉回千兆。用 DKMS 装能一次性解决「内核升级后驱动丢失」的问题。

包里的dkms.conf大致长这样:

PACKAGE_NAME="r8125" PACKAGE_VERSION="9.007.01" CLEAN="make -C src clean" MAKE[0]="make -C src all" BUILT_MODULE_NAME[0]="r8125" DEST_MODULE_LOCATION[0]="/kernel/drivers/net/ethernet/realtek" AUTOINSTALL="yes"

CLEAN和MAKE[0]是 DKMS 构建时执行的命令,必须用相对路径,因为 DKMS 会把源码复制到/var/lib/dkms/r8125/9.007.01/build/下再执行。BUILT_MODULE_NAME[0]是模块名,不带.ko后缀。DEST_MODULE_LOCATION[0]决定装到/lib/modules/$(uname -r)/下的哪个子目录,这里选了realtek目录,和r8169同级,方便统一管理。

注册并安装:

sudo mkdir -p /usr/src/r8125-9.007.01 sudo cp -r ./* /usr/src/r8125-9.007.01/ sudo dkms add -m r8125 -v 9.007.01 sudo dkms build -m r8125 -v 9.007.01 sudo dkms install -m r8125 -v 9.007.01 --force # 确认状态 dkms status # r8125/9.007.01, 4.19.90-xx.el8.x86_64, x86_64: installed

dkms add只是把源码登记到一个管理列表里,build才真正编译,install会调用depmod把模块写进索引。--force是有必要的,重装同版本或者覆盖已有模块时,DKMS 默认会拒绝,加这个参数强行覆盖。dkms status的输出能看出三个信息:包名版本、针对哪个内核构建、当前状态。状态是installed才算成功,出现built说明只编了没装。

以后内核升级,dkms服务在启动时会自动为每个登记过的模块跑一次build和install,这就是放弃手工cp的核心理由。

4.3 黑名单 r8169 与链路速率验证

DKMS 装完,还得阻止r8169抢设备。做法是写一个 modprobe 配置文件:

# 屏蔽 r8169,避免它抢先绑定 10ec:8125 sudo tee /etc/modprobe.d/r8125-blacklist.conf >/dev/null <<'EOF' blacklist r8169 install r8169 /bin/true EOF # 立即生效:先卸载 r8169,再加载 r8125 sudo modprobe -r r8169 sudo modprobe r8125

blacklist只是让modprobe在自动加载阶段跳过这个模块,如果别的模块依赖它,还是会被拉起来。所以加一行install r8169 /bin/true,这条规则会在任何加载r8169的尝试上执行/bin/true,等于永远加载不了。两行一起写才算彻底。

注意卸载顺序:必须先把r8169卸掉,设备才会释放,r8125才能 probe 成功。如果反过来,你会看到modprobe: ERROR: could not insert 'r8125': Device or resource busy。

验证从三个层面看,从下往上:

# 1. PCI 层面:确认设备被 r8125 接管 lspci -nnk | grep -A3 -i '10ec:8125' # Kernel driver in use: r8125 # 2. 驱动层面:确认加载的是你自己的模块 ethtool -i enp4s0 # driver: r8125 # version: 9.007.01 # 3. 链路层面:确认协商速率 ethtool enp4s0 | grep -i speed # Speed: 2500Mb/s

lspci -nnk里的Kernel driver in use是最有说服力的一行,它直接反映内核当前把设备绑给了哪个驱动。ethtool -i看驱动名和版本,能确认用的不是系统自带的老版本。ethtool不带参数输出的Speed字段,需要交换机或者对端也支持 2.5G 才能协商到 2500Mb/s,如果显示1000Mb/s,先换根网线——很多老线只能稳住千兆。

5. 避坑排查:编译失败和装完不生效的 5 种现场

5.1 make 报 /lib/modules/xxx/build 不存在

现象:敲下make立刻报make: *** /lib/modules/4.19.90-xx.x86_64/build: No such file or directory. Stop.,连编译都没开始。

原因:kernel-devel没装,或者装的是另一个版本的kernel-devel,软链没建立起来。系统只装了kernel-headers也不够,那个包只提供用户态头文件,不包含构建内核模块需要的 Makefile 和 Kconfig。

解决:按uname -r的精确版本装 devel 包,别用不带版本号的包名。

sudo rpm -ivh kernel-devel-$(uname -r).rpm ls -l /lib/modules/$(uname -r)/build # 必须指向有效目录

如果仓库里没有这个精确版本,说明系统内核被升级过但旧包被清理了,要么回退内核,要么找对应的 devel 包离线补上。

5.2 编译到一半报结构体成员不存在

现象:error: 'struct net_device' has no member named 'trans_start',或者too few arguments to function 'netif_napi_add'这类的编译错误。

原因:源码和内核 API 版本对不上。trans_start在 5.x 早期被移除了,netif_napi_add在新内核里增加了一个权重参数。你拿的是 A 版本内核的驱动源码,编的是 B 版本的内核。

解决:优先换成配套版本的源码,而不是硬改代码。如果确实只能用这份源码,就针对报错点逐个适配:

/* 老代码:直接访问 trans_start */ // dev->trans_start = jiffies; /* 新内核适配:用 netif_trans_update 封装 */ netif_trans_update(dev); /* netif_napi_add 从 3 参数变 4 参数,多出来的是权重 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) netif_napi_add(adapter->netdev, &adapter->napi, e1000e_poll, 64); #else netif_napi_add(adapter->netdev, &adapter->napi, e1000e_poll, 64); #endif

用LINUX_VERSION_CODE做条件编译是驱动里最常见的兼容手法,一份代码能同时过多个内核版本。代价是代码会变脏,所以只在必须维护多版本时才这么做。

5.3 装完以后网卡还是走老驱动

现象:dkms status显示 installed,modprobe r8125也没报错,但ethtool -i eth0里的 driver 还是r8169,速率卡在 1000Mb/s。

原因:r8169在内核启动早期就已经 probe 绑定了设备,r8125加载时发现设备被占用,probe 直接失败,但modprobe本身返回成功(模块加载和驱动绑定是两件事)。dmesg里会有r8125: probe of 0000:04:00.0 failed with error -16。

解决:黑名单加上强制加载屏蔽,然后重建 initramfs,让屏蔽在启动阶段就生效。

sudo tee /etc/modprobe.d/r8125-blacklist.conf >/dev/null <<'EOF' blacklist r8169 install r8169 /bin/true EOF # 关键:initramfs 里也带着 r8169,必须重建,否则早期启动阶段还是它 sudo dracut -f /boot/initramfs-$(uname -r).img $(uname -r) sudo reboot

重建 initramfs 这一步最容易被跳过。只改/etc/modprobe.d/而不重建 initramfs,重启后早期用户空间加载的仍然是旧 initramfs 里那套模块配置,屏蔽规则根本没读进去。重启后拿lsmod | grep -E 'r8125|r8169'确认只有r8125在。

5.4 insmod 报 Required key not available

现象:sudo insmod r8125.ko报insmod: ERROR: could not insert module r8125.ko: Required key not available。

原因:系统开了 Secure Boot,内核只接受用受信任密钥签过名的模块,你自己编的.ko没有签名。这是唯一一种「编译完全正确但就是加载不进去」的情况,第一次碰到很容易怀疑人生。

解决:给模块签名,把公钥导入 MOK 列表。

# 1. 生成自签名密钥(只做一次,密钥要保存好) openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv \ -outform DER -out MOK.der -nodes -days 36500 -subj "/CN=Local Driver Signing/" # 2. 编译时带上签名参数,或者对已有 ko 单独签 /usr/src/kernels/$(uname -r)/scripts/sign-file sha256 \ MOK.priv MOK.der r8125.ko # 3. 导入公钥,这一步会要求设置一个一次性密码,重启时在蓝色界面输入 sudo mokutil --import MOK.der sudo reboot

重启后会进入 MOK 管理界面,选 Enroll MOK,输入刚才设的密码。sign-file脚本在内核源码的scripts/目录下,来自kernel-devel包。如果不想折腾签名,也可以在 BIOS 里关掉 Secure Boot,但这样整机安全性会下降,内网机器上我不推荐。

5.5 链路起来了但速率不对

现象:ip a能看到网卡,ethtool显示Link detected: yes,但Speed只有 1000Mb/s 甚至 100Mb/s,双向打流跑不满。

原因:链路速率是两端协商出来的,驱动正常不代表能协商到 2.5G。常见因素有三个:网线是超五类且线序有问题只能稳千兆、对端交换机端口不支持 2.5G、网卡节能特性导致降速。

解决:先排除物理层,再看驱动设置。

# 看协商能力,Supported link modes 里有没有 2500baseT/Full ethtool eth0 # 强制指定速率和双工,排除自协商问题(对端也必须固定同样配置) sudo ethtool -s eth0 speed 2500 duplex full autoneg off # 如果是功耗特性导致,关掉 EEE sudo ethtool --set-eee eth0 eee off

先看Supported link modes,如果里面压根没有 2500baseT,说明驱动或者硬件层面就没识别成 2.5G 设备,回头查lspci -nnk的驱动绑定。如果支持但协商不上去,换线换口是最快的验证方式。

6. 二次开发:把驱动构建固化成一个可复现的流程

这套东西用一次就完了吗?不是。只要机器还在跑,内核就会升级,每次dnf update都可能让手工cp进去的.ko失效。我踩过一次坑:给一批机器装完 r8125,三个月后统一更新内核,第二天反馈「网卡变慢了」。登上去一看,r8169又回来了,驱动目录里空空的。

从那以后我的做法是:任何手工编译的内核模块,一律走 DKMS 登记,绝不直接cp。e1000e 也一样,虽然它可能系统自带,但如果你改了参数或者打补丁,同样需要 DKMS 管理。把源码放到/usr/src/<pkg>-<ver>/,写一个dkms.conf,后面所有事交给dkms服务。

在此基础上还可以再往前一步,把构建过程脚本化,保证换台机器也能一次跑通:

#!/bin/bash # build-drivers.sh - 在目标机上一次性构建并登记两个驱动 set -euo pipefail KVER=$(uname -r) SRC_ROOT=/usr/src build_one() { local name=$1 ver=$2 dir=$3 rm -rf "${SRC_ROOT}/${name}-${ver}" cp -r "${dir}" "${SRC_ROOT}/${name}-${ver}" dkms remove -m "${name}" -v "${ver}" --all 2>/dev/null || true dkms add -m "${name}" -v "${ver}" dkms build -m "${name}" -v "${ver}" dkms install -m "${name}" -v "${ver}" --force } build_one e1000e 3.8.4 ./e1000e-src build_one r8125 9.007.01 ./r8125-9.007.01 dkms status

set -euo pipefail让脚本在任何一步失败时立刻退出,不至于带着半成品继续往下走。build_one里先remove --all再add,是为了让脚本可以重复执行,不用每次手工清理旧登记。--force应对同版本重装。

如果要做批量部署,把这段脚本加上两个驱动源码目录打成 tar 包,一次拷到目标机解压执行即可,不需要联网。

再进一步是打补丁的场景。比如你发现某个 PHY 在特定主板上偶尔失联,需要在phy.c里加一个 workaround。正确做法是把改动做成 patch 文件,放在源码目录同级,在 DKMS 的PRE_BUILD里打上去:

PACKAGE_NAME="e1000e" PACKAGE_VERSION="3.8.4-patched" MAKE[0]="make -C /lib/modules/${kernelver}/build M=$PWD modules" PRE_BUILD="patch -p1 < ../0001-phy-workaround.patch || true"

这样源码目录保持干净,补丁内容单独可审计,换人接手时一眼能看出改了什么。|| true是为了让重复构建时不因为补丁已打过而中断,代价是补丁应用失败也会被吞掉——更好的写法是判断返回值,但作为模板先这样。

验证方式很简单,每次改完跑一遍dkms status加ethtool -i加ethtool eth0 | grep Speed,三行输出全对才算过。我现在每次动驱动,哪怕只改一个参数,也会把这三条命令走一遍再下班,省得第二天早上被电话叫醒。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询