银河麒麟V10网卡驱动编译排障:e1000e与RTL8125内核对齐
简介:面向在银河麒麟V10上部署有线网络的运维人员与驱动开发者,提供两份可编译通过的网卡驱动源码:Intel e1000e 3.8.4 与 Realtek RTL8125。针对国产系统内核适配中的兼容问题,源码已删除重复定义、调整函数参数,可直接进入编译与加载流程,适用于政府、企业等自主可控环境下的服务器与终端网卡识别、网络连通性恢复。
压缩包为 tgz 格式,共 56 个文件、483KB,以 21 个 .h 头文件与 19 个 .c 源文件为主体,配套 Makefile、makefile_linux24x 等构建脚本,README、log.txt、SUMS 等说明与校验文件,以及 spec、COPYING 等打包元数据,整体沿用官方驱动源码布局,便于对照阅读与二次修改。
目前已有 2750 人学习下载。读者可拿到两份已适配银河麒麟V10 的驱动源码,省去排查命名冲突与函数接口不匹配的反复试错,并借助构建脚本和说明文件完成编译、加载与网卡验证,可作为国产化替代项目中网卡驱动移植的参考。
1. 先别急着敲 make:银河麒麟 V10 上网卡驱动最容易栽在版本对齐
银河麒麟 V10 装完,板载千兆口能亮,新插的 RTL8125 2.5G 网卡却在ip link里连影子都没有;另一台机器 e1000e 倒是认到了,链路却反复 up/down,dmesg里刷一片 reset。这两类现场我一年能碰上十几次,根子几乎都不是「驱动没装」,而是网卡驱动源码和当前内核对不上。
V10 不同更新批次的内核不是一条线,4.19 和 5.4 两条分支都跑过,同一份 r8125 源码包在 A 机器上一次过,在 B 机器上直接报struct net_device has no member named ...。所以这套环境真正的门槛不在编译命令,而在「先确认内核,再选源码」。适合打算把 V10 当网关、软路由、内网文件服务器用的人,双网口是刚需,而这两个驱动恰好是绕不过去的两块砖。
2. 编译环境搭建:gcc、kernel-devel 与内核头文件的三方对齐
装驱动之前,先把机器的底细摸清楚。我见过太多人一上来就解压源码包敲make,报错之后再回头查内核,来回折腾两小时。正确的顺序是:确认内核版本 → 确认网卡型号 → 装匹配版本的编译依赖 → 校验头文件路径能通。这四步里任何一步跳过,后面都会以「一堆看不懂的编译错误」的形式还回来。
2.1 摸清内核版本与网卡型号的组合
先确认三件事:内核版本、系统版本、网卡芯片型号。这三者决定了你要拿哪一份源码。
# 内核版本,后面所有路径都依赖这个字符串 uname -r # 系统版本,确认是 V10 的哪个更新批次 cat /etc/kylin-release # 列出所有网络控制器,看厂商 ID 和设备 ID lspci -nn | grep -i -E "ethernet|network" # 看当前是谁在驱动这张卡 lspci -k | grep -A 3 -i ethernetuname -r的输出形如5.4.18-xx-generic,前面是内核主版本,后面的-xx是发行版补丁号。编译驱动时,/lib/modules/$(uname -r)/build这个符号链接必须指向同主版本的内核头文件目录,否则后面make -C那一句会直接失败。
lspci -nn里方括号中的[8086:xxxx]或[10ec:xxxx]是厂商 ID 加设备 ID。e1000e 覆盖的是板载千兆那一类,r8125 对应的是 2.5G 那个口。如果lspci -k显示Kernel driver in use: r8169而你的卡其实是 RTL8125,说明系统在内核自带的兜底驱动上跑着,能通但性能打折,这时候才需要换厂商驱动。
2.2 装编译工具链:少一个包就卡在 modpost
V10 走的是 RPM 系包管理,yum和dnf都能用。下面这一条把编译内核模块需要的依赖一次装齐:
yum install -y \ gcc gcc-c++ make \ kernel-devel-$(uname -r) \ kernel-headers-$(uname -r) \ elfutils-libelf-devel \ openssl-devel \ dkms ethtool pciutils net-tools几个包的作用得说清楚,不然报错时你不知道该补哪个:
kernel-devel-$(uname -r):提供/lib/modules/$(uname -r)/build下的 Makefile 和Module.symvers。版本必须和uname -r完全一致,差一个补丁号都不行,这是最常见的翻车点。kernel-headers:用户态和部分内核态头文件,某些版本的驱动会 include 到。elfutils-libelf-devel:5.4 内核的modpost阶段要读 ELF 符号,缺了它报Cannot use CONFIG_STACK_VALIDATION或者missing libelf。openssl-devel:内核模块签名流程会用到,开了 Secure Boot 的机器没它编译都过不去。dkms:后面 r8125 要用它接管,内核一升级自动重编,属于后悔药级别的工具。
提示:
kernel-devel-$(uname -r)这种写法依赖 shell 展开,如果仓库里没有这个精确版本,yum 会报No package ... available。这时候换成yum install kernel-devel装最新的,再回头看版本对不对得上——对不上就得换路子,见 2.3。
2.3 校验内核头文件路径:build 软链指向哪里
装完包别急着往下走,先做一次路径校验。这一步花三十秒,能省掉后面半小时的排查。
# build 软链应该指向 /usr/src/kernels/ 下对应版本的目录 ls -l /lib/modules/$(uname -r)/build # 看这个目录里有没有关键文件 ls /lib/modules/$(uname -r)/build/ | grep -E "Makefile|Module.symvers" # 确认已安装的 kernel-devel 版本 rpm -qa | grep kernel-devel正常输出应该是build -> /usr/src/kernels/5.4.18-xx-generic这样的软链。如果build不存在,说明kernel-devel压根没装;如果存在但指向的版本和uname -r不一致,那就是装错了版本。
Module.symvers这个文件必须存在。它是内核导出符号表,编译外部模块时用来做符号解析,缺了它编译能过但加载会报Unknown symbol。
下面这张表是我整理的常见组合,遇到对不上的情况按表处理:
| 现场表现 | 根因 | 处理方式 |
|---|---|---|
build软链不存在 | kernel-devel 没装 | 按uname -r精确安装 |
软链指向的版本比uname -r新 | 装包时内核已升级但没重启 | 重启进新内核,或装旧版 kernel-devel |
有 build 目录但无Module.symvers | 只装了 kernel-headers 没装 kernel-devel | 补装 kernel-devel |
编译通过但insmod报Invalid module format | vermagic 不一致 | 说明编译用的不是当前运行内核的头文件 |
还有一种情况:仓库里根本没有你当前内核对应的kernel-devel,比如系统是从旧镜像装的、内核被单独升过。这时候只有两条路,要么把内核降到仓库里有 devel 包的版本,要么去拿一份同版本完整内核源码自己对齐,后者在下一章会用到。
3. 编译 e1000e:从内核源码树里摘出单模块的完整走法
e1000e 的情况和 r8125 不太一样。它是内核主线里就有的驱动,V10 自带的版本通常能用,但新批次的板载千兆网卡在旧版本驱动上会出现识别不全、链路抖动、EEE 协商异常这类问题。重编的目的不是「装一个没有的驱动」,而是「换一个更新的版本覆盖内核自带的那份」。
3.1 准备一份与当前内核同版本的完整源码
kernel-devel包里只有头文件和构建脚本,没有drivers/目录下的驱动源码。所以第一步是拿完整内核源码,版本必须和uname -r的主版本一致。
# 假设 uname -r 输出 5.4.18-xx-generic,主版本就是 5.4.18 cd /usr/src # 源码包来源以你手上有的为准,解压后目录名一般是 linux-5.4.18 tar -xf linux-5.4.18.tar.xz -C /usr/src/ cd /usr/src/linux-5.4.18 # 用当前运行内核的配置做基线,避免配置差异导致的编译分支不同 cp /boot/config-$(uname -r) .config make olddefconfigmake olddefconfig的作用是把新配置项按默认值补齐,不进入交互界面。这一步不能省,因为后面make -C ... M=编译单模块时会读取构建目录里的.config,配置不对会导致某些条件编译的代码路径被裁掉,编出来的模块行为跟预期不一致。
有个细节要盯住:源码版本不必和-xx-generic后面的补丁号完全一致,主版本对上就行。但如果你要编的驱动用到了补丁里新增的 API,那就必须精确匹配,否则会报某个函数未声明。我的习惯是能精确就精确,少一层不确定性。
3.2 用 M= 参数单独编译 e1000e 目录
不用编整个内核,那样一跑就是四十分钟起步。内核构建系统支持只编一个子目录:
cd /usr/src/linux-5.4.18/drivers/net/ethernet/intel/e1000e # -C 指定内核构建目录,M= 指定模块源码目录 # CONFIG_E1000E=m 覆盖配置,强制按模块编译 make -C /lib/modules/$(uname -r)/build M=$PWD CONFIG_E1000E=m modules这三段参数各有含义,改错一个就白跑:
-C /lib/modules/$(uname -r)/build:切到内核构建目录执行 Makefile,用的是已经装好的 kernel-devel,不是上面解压的源码树。这样保证 vermagic 和当前运行内核一致。M=$PWD:告诉构建系统「模块源码在当前目录」,只处理这个子目录,不递归整棵树。CONFIG_E1000E=m:这一句是关键。e1000e 目录下的 Makefile 写的是obj-$(CONFIG_E1000E) += e1000e.o,配置没打开的话obj-m是空的,编译过程会「成功」但不产出任何.ko,很多人卡在这里以为编过了。
编译成功的标志是目录下出现e1000e.ko。顺手看一眼模块信息,确认版本和签名状态:
modinfo e1000e.ko | grep -E "version|vermagic|filename"vermagic那一行必须和uname -r对得上,这是模块能不能加载的硬门槛。
3.3 安装 .ko 与 depmod 的先后顺序
编出来的.ko不能直接insmod了事,重启就没了。要放进模块目录并更新依赖索引。
# 放到 updates 目录,优先级高于内核自带的 kernel/drivers 路径 install -m 644 e1000e.ko /lib/modules/$(uname -r)/updates/ # 重建模块依赖索引 depmod -a # 确认系统现在解析到的是哪一份 modinfo -n e1000edepmod -a会扫描/lib/modules/$(uname -r)/下所有模块,重新生成modules.dep。目录优先级顺序是updates>extra>weak-updates>kernel,所以放updates/能确保覆盖内核自带那份,不用去动/lib/modules/.../kernel/drivers/里的原始文件。这点很重要——直接覆盖原文件,下次内核升级或重装 kernel 包时会被冲掉,而且出问题不好回滚。
modinfo -n e1000e输出的路径应该是/lib/modules/.../updates/e1000e.ko。如果还指向kernel/drivers/...,说明 depmod 没生效或者放错目录了。
然后卸载重载:
# 先卸载,再加载,中间用 ip link 确认网口是否还在 modprobe -r e1000e modprobe e1000e # 看加载日志,重点看版本号和链路协商 dmesg | tail -30 | grep -i e1000emodprobe -r卸载时如果提示Module e1000e is in use,说明网口还在 up 状态或者被 NetworkManager 占着。先ip link set eth0 down,必要时systemctl stop NetworkManager临时停一下,加载完再启回来。
如果要给 e1000e 加参数,写进/etc/modprobe.d/下的配置文件,别手写进 grub:
cat > /etc/modprobe.d/e1000e.conf <<'EOF' # 关闭 S0ix 电源管理,部分平台开启后会出现链路间歇性断开 options e1000e S0ixPowerManagement=0 # 中断节流率,0 表示关闭节流,低延迟场景用 options e1000e InterruptThrottleRate=3000 EOFS0ixPowerManagement这个参数在笔记本平台和部分工控板上是链路抖动的元凶,默认开启时网口在空闲后会进入低功耗状态,唤醒不及时就表现为丢包。InterruptThrottleRate单位是每秒中断数,3000 是折中值,追求吞吐可以调高,追求延迟可以设 0,但设 0 之后小包场景 CPU 占用会明显上升。
4. 编译 r8125:2.5G 网卡驱动的源码编译与 DKMS 接管
RTL8125 在 V10 上的处境比 e1000e 尴尬。内核里没有它的原生驱动,只有r8169这个万能兜底,能点亮、能通、能上网,但 2.5G 速率跑不满,大流量下还容易出 reset。厂商提供的 r8125 源码包是正经解法,难点在于编译参数和内核升级后的维护。
4.1 解包源码并读懂 Makefile 里的开关
源码包通常是r8125-<版本号>.tar.bz2,解压后目录结构是顶层一个 Makefile,加一个src/子目录装真正的驱动代码。
tar -xjf r8125-*.tar.bz2 cd r8125-*/ # 先看顶层 Makefile 和 README,不同版本的编译入口不一样 ls cat README* 2>/dev/null | head -40顶层 Makefile 一般只做转发,真正的编译规则在src/Makefile里。编译之前值得花两分钟看一眼里面有哪些可配的宏,比如是否启用多队列、是否编译调试信息。不同版本的包内容差异不小,别照抄网上的命令,以包内 README 为准。
编译命令本身很简单:
# 顶层 Makefile 转发到 src 目录编译 make -C src # 或者直接进 src 目录 cd src && make产物是src/r8125.ko。如果编译报错,八成是源码版本旧于内核版本,某个内核 API 签名变了。这类错误的特征是报struct xxx has no member named yyy或者too few arguments to function,处理办法是换一个更新的源码包版本,而不是去改驱动代码——改了一处还有十处,改不完的。
4.2 make install 与 initramfs 更新
厂商包一般自带 install 目标,会把.ko拷到正确位置并跑 depmod:
make -C src install如果包比较老、没有 install 目标,就手动来:
install -m 644 src/r8125.ko /lib/modules/$(uname -r)/updates/ depmod -a接下来是最容易被忘的一步:把 r8169 拉黑。内核自带的 r8169 会抢注 RTL8125 设备,谁先加载谁占坑。不拉黑的话,你装完 r8125、重启,lspci -k里还是显示r8169,白忙一场。
cat > /etc/modprobe.d/blacklist-r8169.conf <<'EOF' # 阻止内核自带 r8169 抢占 RTL8125 设备 blacklist r8169 EOF # 更新 initramfs,让黑名单在启动早期就生效 dracut -f /boot/initramfs-$(uname -r).img $(uname -r)dracut那句不能省。modprobe.d里的黑名单在内核启动后期才读取,而 r8169 有可能在 initramfs 阶段就被加载了。不重建 initramfs,黑名单形同虚设。V10 上如果dracut不可用,用mkinitrd -f效果一样。
做完之后重载模块验证:
modprobe -r r8169 2>/dev/null modprobe r8125 dmesg | grep -i r8125 | tail -20日志里应该能看到芯片型号、固件版本、链路速率。如果提示需要固件文件,从系统的 firmware 包里找对应的rtl_nic/文件补进去,路径一般是/lib/firmware/rtl_nic/。
4.3 用 DKMS 接管,避免内核一升级驱动就失踪
手动编译装进去的模块有个致命问题:内核一升级,/lib/modules/$(新版本)/下没有这个.ko,网卡直接失联。远程机器上遇到这种情况,基本只能跑机房。DKMS 就是为这个场景设计的。
# 把源码放进 DKMS 的标准路径 mkdir -p /usr/src/r8125-<版本号> cp -r src /usr/src/r8125-<版本号>/ cp dkms.conf /usr/src/r8125-<版本号>/ 2>/dev/null # 注册、编译、安装 dkms add -m r8125 -v <版本号> dkms build -m r8125 -v <版本号> dkms install -m r8125 -v <版本号> # 看状态 dkms statusdkms.conf里的关键字段是这几个:
PACKAGE_NAME="r8125" PACKAGE_VERSION="<版本号>" BUILT_MODULE_NAME[0]="r8125" DEST_MODULE_LOCATION[0]="/kernel/drivers/net/ethernet/realtek" AUTOINSTALL="yes"DEST_MODULE_LOCATION要和系统里实际路径习惯一致,AUTOINSTALL="yes"保证以后装新内核时自动重编。包里如果自带dkms-install.sh,直接跑脚本更省事,但跑完记得dkms status确认一遍,脚本失败不报错的坑我踩过。
内核升级后,dkms status应该显示新内核版本下也是installed。如果显示built但没installed,手动dkms install一次就行。
5. 避坑与排查:装完驱动不认卡的五种现场
驱动编译通过、安装完成、也depmod了,网口还是不见踪影——这是最让人抓狂的阶段。下面五条是按出现频率排的,每条都按现象、原因、解决三段写,照着对号入座。
5.1 现象:modprobe报 Required key not available
原因:主板开了 Secure Boot,内核处于强制签名模式,未签名的第三方模块一律拒绝加载。
解决:两条路。一是进 BIOS 关掉 Secure Boot,最省事,缺点是有些合规环境不允许。二是给模块自签名,用mokutil把公钥导入 MOK 列表:
# 生成签名密钥 openssl req -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER \ -out MOK.der -nodes -days 36500 -subj "/CN=Local Module Signing/" # 给模块签名 /usr/src/kernels/$(uname -r)/scripts/sign-file sha256 MOK.priv MOK.der /path/to/r8125.ko # 导入公钥,执行后需要重启并在蓝色界面确认 mokutil --import MOK.der重启时会进一个 MOK 管理界面,按提示输入一次性密码确认。之后这批签过名的模块就能加载了。注意每次重编模块都要重新签名,所以配 DKMS 的话要在dkms.conf里加POST_BUILD钩子自动签。
5.2 现象:ip link里只有 r8169,看不到 r8125
原因:内核自带驱动抢先占坑,或者黑名单没在 initramfs 阶段生效。
解决:确认三件事。lsmod | grep -E "r8125|r8169"看谁在跑;modinfo -n r8125确认模块路径存在;cat /etc/modprobe.d/blacklist-r8169.conf确认黑名单文件在。三步都对还不行,就是 initramfs 的问题,重新跑dracut -f,然后确认lsinitrd /boot/initramfs-$(uname -r).img | grep r8169里没有 r8169 的影子。
5.3 现象:insmod报 Invalid module format
原因:编译用的内核头文件版本和当前运行内核不一致,vermagic对不上。
解决:跑modinfo r8125.ko | grep vermagic和uname -r对比。不一致说明编译时-C指向的build目录不是当前内核的。检查/lib/modules/$(uname -r)/build软链指向,必要时重装匹配版本的kernel-devel再编一次。这个坑在多内核机器上特别常见——装了新内核没重启,编译时用的却是旧内核的头文件。
5.4 现象:链路起来了但只有 100Mbps
原因:不是驱动问题,是物理层协商。网线是 Cat5e 以下、交换机端口只支持百兆、或者 EEE 节能以太网协商异常。
解决:先用ethtool eth0看Supported link modes里有没有 2500baseT,有的话说明网卡能力没问题。然后换一根 Cat6 线、换交换机端口试。如果硬件都没问题,关掉 EEE 再试:
ethtool --set-eee eth0 eee off # 或者写进模块参数,重启后依然生效 echo 'options r8125 eee_enable=0' > /etc/modprobe.d/r8125.conf也可以强制指定速率双工做排除法:ethtool -s eth0 speed 2500 duplex full autoneg off。能起来说明是自协商的问题,起不来就是物理链路的问题。
5.5 现象:能通但大流量下丢包、延迟抖动
原因:中断全压在 CPU0 上,多队列没启用,或者 ring buffer 太小导致溢出。
解决:先看中断分布cat /proc/interrupts | grep -E "r8125|e1000e",如果全在同一列,说明 RSS 没生效。用ethtool -l eth0看队列数,ethtool -L eth0 combined 4调上去(不超过硬件支持上限)。再看丢包计数:
ethtool -S eth0 | grep -iE "drop|error|miss" ethtool -g eth0rx_missed_errors持续增长就是 ring buffer 不够,用ethtool -G eth0 rx 1024 tx 1024调大。上限由芯片决定,ethtool -g会显示Pre-set maximums。调完记得写进开机脚本,重启会复位。
6. 验证跑通:用 ethtool、dmesg 和 iperf3 做一遍闭环
驱动装完不算完,得有一遍能说服自己的验证。我的顺序是:先看模块身份,再看链路协商,再看统计计数,最后压一遍流量。
第一步,确认驱动身份是对的:
# 看驱动名、版本、固件版本、总线位置 ethtool -i eth0 # 看内核认到的驱动 lspci -k | grep -A 3 -i ethernetethtool -i输出里driver: r8125和version:两行是关键,版本号要和你编译的源码包对得上。如果显示的还是r8169,回第 5 章第 2 条。
第二步,看链路协商结果:
ethtool eth0 | grep -E "Speed|Duplex|Link detected|Supported link modes"Speed: 2500Mb/s、Duplex: Full、Link detected: yes,三个都对才算物理层通了。千兆口则是1000Mb/s。
第三步,看统计计数有没有异常增长:
# 清空计数,压一段流量后再看 ethtool -S eth0 | grep -iE "error|drop|miss"清空计数不同驱动支持程度不一样,ethtool -S只读的情况下就直接记下当前值,压完再对比差值。rx_errors、rx_missed_errors、tx_dropped这三个涨得快就有问题。
第四步,压流量。两台机器直连或者过交换机,一端跑服务端一端跑客户端:
# 服务端 iperf3 -s # 客户端,30 秒,4 条并发流 iperf3 -c 192.168.1.10 -t 30 -P 42.5G 口单流一般在 1.5~2.3 Gbps 之间,多流能跑到 2.3 Gbps 以上。千兆口跑满就是 940 Mbps 左右,这是 TCP 开销决定的,跑不到 1000 不是驱动问题。如果单流只有几百兆、多流上不去,回第 5 章第 5 条查队列和 ring buffer。
下面这张表是我用来快速判读的,几个关键输出对上就基本能定性:
| 检查项 | 正常值 | 异常指向 |
|---|---|---|
ethtool -i的 driver | r8125 / e1000e | 驱动没接管,查黑名单 |
ethtool的 Speed | 2500Mb/s 或 1000Mb/s | 物理层,查线材和端口 |
dmesg里的 reset 计数 | 无或极少 | 电源管理,关 S0ix/EEE |
rx_missed_errors | 不增长 | ring buffer 太小 |
| iperf3 多流吞吐 | 大于单流的 1.3 倍 | 多队列没生效 |
最后补一句长期维护的事:手动装的两个驱动,内核一升级就得重来一遍。所以我的习惯是 e1000e 走updates/目录加一个记录脚本,r8125 一律用 DKMS 接管。升级内核前先dkms status看一眼,升级后modinfo确认路径,两步都过了再重启——远程机器上这一步能救命。
从那以后我每次装完这两个驱动,都会强制走一遍「ethtool -i 看身份、ethtool 看速率、iperf3 压三十秒」的闭环,不管机器是不是在内网、有没有人催。少一次验证,后面就多一次半夜被叫起来查丢包。希望帮到你。
本文还有配套的精品资源,点击获取