ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

银河麒麟V10下e1000e/RTL8125驱动编译与加载排错

银河麒麟V10下e1000e/RTL8125驱动编译与加载排错

简介:面向银河麒麟V10国产化环境开展网卡驱动适配的运维与开发人员,这份源码包提供已在系统中编译通过的e1000e与RTL8125驱动,可用于解决Intel千兆网卡及Realtek RTL8125B/C网卡在该平台上的识别、加载与联网问题。包体共56个文件,以C源码、头文件为主,辅以Makefile构建脚本、README说明、spec打包描述及补丁与兼容层文件,压缩后约483KB,目录按两套驱动源码分包组织,便于对照内核接口与编译配置。已有2750人学习下载。其价值在于给出可复现的编译基础:e1000e部分涉及源码重复定义清理,RTL8125部分涉及函数参数调整,配合构建脚本和说明文档,读者可据此完成源码审查、编译加载与基本连通性验证,也能为同类信创平台的驱动移植和排错提供参考。

1. 银河麒麟 V10 编译 e1000e/RTL8125 驱动:别先改源码,先看内核头文件

银河麒麟 V10 下编译 e1000e 和 RTL8125 网卡驱动,最容易翻车的不是驱动源码本身,而是内核头文件、内核版本和签名这三道关。很多人拿到源码直接make,看到make: *** /lib/modules/.../build: No such file or directory就以为源码缺文件;也有人编译通过,insmod却报Invalid module format。这类故障适合正在给老机器补 2.5G 网卡、或者把 e1000e 驱动替换成新版本的人。先别急着改 C 代码,按内核头文件、源码 API、模块加载三层往下排,后面会少踩很多坑。RTL8125 这类 2.5G 网卡和 e1000e 这类千兆网卡驱动,在银河麒麟 V10 的不同 SP 版本上内核差异不小,报错长得像,根因经常不在一层。

2./lib/modules/$(uname -r)/build不存在:内核头文件缺失与版本错位

2.1 报错现场:make 停在不存在的 build 目录

最常见的现场是进了驱动源码目录,直接敲make,第一行就停:

make: *** /lib/modules/$(uname -r)/build: No such file or directory. Stop.

这时候不要急着怀疑源码缺文件。先把$(uname -r)展开成实际内核版本,再看/lib/modules/下面到底有什么。我一般会先跑三条命令:uname -r看当前运行内核,ls -l /lib/modules/$(uname -r)/build看软链指向,ls /usr/src/看头文件目录在不在。三條命令一对照,问题基本就分成两类:一类是头文件包根本没装,另一类是装了但装的是别的内核版本,build软链断掉。

# 当前运行内核 uname -r # build 软链是否存在、指向哪里 ls -l /lib/modules/$(uname -r)/build # 头文件目录是否真的存在 ls -d /usr/src/linux-headers-$(uname -r) 2>/dev/null

上面这段没有编译动作,只是把“运行内核”和“头文件目录”对齐。uname -r输出的是当前启动的内核版本;/lib/modules/$(uname -r)/build正常应该是一个软链,指向/usr/src/linux-headers-$(uname -r)一类的目录。如果ls -l显示No such file or directory,说明这个软链不存在;如果软链存在但ls -d找不到目标,说明软链是断的。两种情况的处理不一样,后面分开说。

2.2 根因:头文件包没装,还是内核版本对不上

外部模块编译不是只靠 gcc 和 make,它需要当前内核的配置、头文件和Module.symvers。内核头文件包的名字通常带内核版本,比如桌面版常见linux-headers-$(uname -r),服务器版常见kernel-devel-$(uname -r)。很多人只装了linux-headers-generic或者kernel-devel不带版本,结果装上去的是仓库里最新内核的头文件,而机器实际启动的还是老内核。build软链指向新内核目录,编译出来的模块 vermagic 也是新内核,加载时当然对不上。

还有一种情况是系统刚升级过内核,/lib/modules/下有两个版本目录,但/usr/src/只保留了新版本头文件,老内核的build软链就断了。这个时候make报的是 build 不存在,根因却是“头文件和运行内核错位”。所以不要只看报错行,先确认uname -r和/usr/src/下的目录名是不是同一个版本。

2.3 解决:装对 headers,再验证 build 指向

确认是头文件缺失后,按系统的包管理器装对应版本。银河麒麟 V10 桌面版常见 Debian 系包管理,服务器版常见 RHEL 系包管理,先看/etc/os-release再选命令,不要混用。

# Debian/Ubuntu 系常见安装方式 sudo apt update sudo apt install -y linux-headers-$(uname -r) build-essential # RHEL/CentOS 系常见安装方式 sudo yum install -y kernel-devel-$(uname -r) kernel-headers-$(uname -r) gcc make # 装完立即验证 ls -l /lib/modules/$(uname -r)/build test -f /lib/modules/$(uname -r)/build/Makefile && echo "build ok" test -f /lib/modules/$(uname -r)/build/Module.symvers && echo "symvers ok"

这段命令的逻辑是先把包名锁到当前内核版本,再检查三个东西:build软链、Makefile、Module.symvers。包名里的$(uname -r)不能省,省了就可能装成最新内核的头文件。build-essential提供 gcc、make 等基础工具;kernel-headers提供用户态头文件,kernel-devel提供内核构建目录。Module.symvers很关键,它记录内核符号版本,缺失时 modpost 会警告,严重时模块加载报版本不一致。test -f只做存在性检查,返回build ok和symvers ok才算基础环境齐了。如果Module.symvers缺失,优先换完整的kernel-devel包,不要从别的内核目录复制一个过来凑数,复制出来的符号版本大概率对不上。

2.4 参数说明:-C和M=到底在做什么

头文件装好之后,编译外部模块不要直接make,用内核构建目录来驱动:

# 在驱动源码目录执行 make -C /lib/modules/$(uname -r)/build M=$PWD modules

-C是让 make 先切换到内核构建目录,读内核顶层的 Makefile;M=$PWD告诉内核构建系统“外部模块源码在当前目录”;modules是构建目标。这样编译出来的模块才会带上当前内核的配置和符号版本。直接make只读驱动自己的 Makefile,如果驱动顶层 Makefile 没写对KDIR,就会落到默认路径或者空路径,报错看起来像源码问题,实际是构建入口不对。

如果驱动自带 Makefile,检查里面的KDIR是不是用了$(shell uname -r)。有些源码包为了兼容,把KDIR写死成/lib/modules/$(shell uname -r)/build,这种一般没问题;写死成某个固定路径的,换机器就翻车。失败时先看第一条error,不要看最后一行make: ***,最后一行通常只是“上层目标失败”的汇总。

2.5 什么时候需要软链,什么时候不要碰

只有确认/usr/src/linux-headers-$(uname -r)存在,而/lib/modules/$(uname -r)/build缺失或断链时,才考虑手动补软链:

# 先确认目标目录存在 ls -d /usr/src/linux-headers-$(uname -r) # 再建软链,-f 覆盖旧链接,-n 避免把软链当目录 sudo ln -sfn /usr/src/linux-headers-$(uname -r) /lib/modules/$(uname -r)/build # 验证链接最终指向 readlink -f /lib/modules/$(uname -r)/build

ln -sfn里-s建软链,-f覆盖已有文件,-n防止目标本身是软链时被当成目录处理。readlink -f会展开到最终路径,返回空就说明还是断链。这个操作不是常规步骤,发行版头文件包一般会自动建好软链;手动补链只适合自己编译内核源码到/usr/src/linux的场景。链错比没链更难查,因为make可能不报 build 不存在,而是报一堆头文件里的类型缺失,看着像驱动源码不兼容。

注意:不要从另一个内核版本复制build目录或Module.symvers来“先编过再说”,编出来的模块加载时会报module_layout版本不一致,问题只是从编译期推迟到加载期。

3. RTL8125 编译报implicit declaration:旧驱动撞上新内核 API

3.1 报错现场:RTL8125 源码在pci_alloc_consistent处停住

RTL8125 的驱动源码如果来自较早的版本,在银河麒麟 V10 较新内核上编译,常见报错不是找不到文件,而是函数声明对不上:

r8125_n.c: In function ‘rtl8125_init_one’: r8125_n.c: error: implicit declaration of function ‘pci_alloc_consistent’; did you mean ‘dma_alloc_coherent’? r8125_n.c: error: too few arguments to function ‘netif_napi_add’ r8125_n.c: error: ‘init_timer’ undeclared

这类报错有一个共同点:源码里调的接口在当前内核头文件里已经改了签名或者被移除。implicit declaration不是“少 include 一个头文件”这么简单,很多时候是接口本身没了。继续往下编,还会出现assignment from incompatible pointer type、unknown type name之类的连锁错误。看到第一屏报错就停,先把第一处 API 对不上的位置找出来。

3.2 根因:源码里的内核 API 比当前内核老

RTL8125 驱动是外部模块,它依赖内核内部 API。内核升级时,这些 API 不会保持永久兼容。常见变化有几类:pci_alloc_consistent这类 PCI DMA 接口逐步被dma_alloc_coherent取代;netif_napi_add的 weight 参数在新内核里被拆到netif_napi_add_weight;init_timer被timer_setup取代;ndo_tx_timeout的返回值、skb_shinfo的访问方式也改过。驱动源码如果按老内核写,编译时就会在这些点上报错。

这里要区分“驱动源码太老”和“内核头文件不完整”。如果报错集中在linux/module.h、linux/netdevice.h找不到,先回头查第 2 章的 headers;如果报错集中在驱动自己的.c文件里,函数名和参数对不上,基本就是 API 兼容问题。前者是环境没搭好,后者是源码要适配,处理顺序不能反。

3.3 解决:先换驱动版本,再打兼容补丁

第一选择不是一行行改驱动,而是找一份已经适配当前内核的 RTL8125 源码。外部驱动动辄几千行,API 变化可能涉及十几处,手工补容易漏。资源里整理的可编译版本就是按这个思路来的:先把源码版本和内核 API 对齐,再保留必要的兼容宏。只有找不到合适版本时,才在驱动自己的头文件里做薄封装。

#include <linux/version.h> #include <linux/dma-mapping.h> #include <linux/netdevice.h> /* 老接口 pci_alloc_consistent 在新内核用 dma_alloc_coherent 替代 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) #define rtl8125_pci_alloc_consistent(pdev, size, dma) \ pci_alloc_consistent(pdev, size, dma) #else #define rtl8125_pci_alloc_consistent(pdev, size, dma) \ dma_alloc_coherent(&(pdev)->dev, size, dma, GFP_KERNEL) #endif /* netif_napi_add 的 weight 参数在新内核被拆到 netif_napi_add_weight */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) #define rtl8125_napi_add(dev, napi, poll, weight) \ netif_napi_add_weight(dev, napi, poll, weight) #else #define rtl8125_napi_add(dev, napi, poll, weight) \ netif_napi_add(dev, napi, poll, weight) #endif

这段兼容层的关键是“只包一层,不改调用语义”。LINUX_VERSION_CODE来自linux/version.h,用KERNEL_VERSION做版本比较;dma_alloc_coherent的第一个参数是设备结构,所以从pci_dev取&pdev->dev,GFP_KERNEL表示可睡眠上下文分配。netif_napi_add_weight在较新内核里承接原来的 weight 参数,老内核仍然走四参数版本。调用点改成rtl8125_pci_alloc_consistent和rtl8125_napi_add,不要全局替换所有netif_napi_add,否则可能把别的模块接口也改掉。兼容宏放在驱动自己的头文件里,不要改内核源码树。

3.4 编译参数和验证:外部模块构建

RTL8125 源码顶层 Makefile 要确认KDIR和obj-m没写错:

obj-m += r8125.o r8125-objs := r8125_n.o r8125_rss.o KVER := $(shell uname -r) KDIR := /lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean

obj-m声明要构建成模块的目标,r8125-objs把多个源文件链接成一个r8125.ko。KVER用uname -r取当前内核,KDIR指向当前内核构建目录;$(MAKE)调用 make 自身,避免递归时丢失环境;M=$(PWD)指定外部模块目录。这个 Makefile 不负责安装,只负责编译和清理,安装动作后面单独做。

# 清掉旧对象,避免跨内核 .o 混入 make clean # 用当前内核构建目录编译 make -C /lib/modules/$(uname -r)/build M=$PWD modules # 看模块版本信息和 vermagic modinfo ./r8125.ko | grep -E 'filename|version|vermagic'

make clean不是可选项,尤其是从别的机器拷来的源码目录,旧.o可能按另一个内核编译过,混进来会让报错位置很怪。modinfo里重点看vermagic,它应该包含当前uname -r的版本信息。如果vermagic不对,不要继续加载,回到第 4 章处理内核版本错位。如果编译通过但modpost报missing MODULE_LICENSE,在驱动源文件里补MODULE_LICENSE("GPL"),许可证声明缺失会影响模块加载和符号导出。

3.5 失败时看dmesg和modpost警告

编译阶段的错误看终端第一处error,加载阶段的错误看dmesg | tail -50。RTL8125 加载失败常见两类:一类是Unknown symbol,说明模块依赖的符号没导出,通常是内核配置或符号版本问题;另一类是r8125: probe of ... failed with error -2,说明驱动入口跑了但设备初始化失败,可能是设备 ID 没匹配、固件缺失或者被别的驱动先占用。modpost的警告不要全忽略,WARNING: modpost: missing MODULE_LICENSE()这种要补,WARNING: Symbol version dump ... not found要回头查Module.symvers。

加载前先确认没有旧模块占着设备。RTL8125 的 2.5G 网卡可能被内核自带的r8169驱动先接管,lsmod | grep r8169能看到。如果r8169已经加载,直接insmod r8125.ko可能不报错但网卡不出现,因为设备已经被前一个驱动 probe 了。处理方式在第 5 章避坑里展开。

4. e1000e 加载报module_layout或Required key not available:编译内核和运行内核不是同一个

4.1 报错现场:disagrees about version of symbol module_layout

e1000e 编译通过,insmod时出现:

insmod: ERROR: could not insert module e1000e.ko: Invalid module format dmesg: e1000e: version magic '...' should be '...' e1000e: disagrees about version of symbol module_layout

Invalid module format是用户态工具给的概括,真正原因在dmesg里。version magic那一行会打印模块编译时的 vermagic 和当前内核期望的 vermagic,两个字符串不一致就加载失败。module_layout是内核导出的符号,disagrees about version of symbol module_layout说明模块的符号版本和当前内核的符号版本对不上。很多人看到这里去改源码,其实源码没问题,是编译环境用错了内核。

4.2 根因:编译时内核源码和运行内核不是同一个

外部模块的 vermagic 来自编译时使用的内核构建目录。make -C /lib/modules/$(uname -r)/build里的uname -r如果和当前运行内核不一致,或者build软链指向了另一个内核,编出来的模块就带另一个内核的版本信息。还有一种情况是编译目录里残留了旧内核的.o和.cmd文件,make没有全部重编,链接时混入了旧对象。CONFIG_MODVERSIONS打开时,内核还会校验符号 CRC,Module.symvers不是当前内核的,也会报module_layout或disagrees about version of symbol。

先对齐三样东西:uname -r的输出、/lib/modules/$(uname -r)/build的最终指向、modinfo ./e1000e.ko里的 vermagic。三者不一致,就不要往下加载。不要用--force强行加载,强制加载可能让内核在后续收包时崩掉,排查成本更高。

4.3 解决:锁定当前内核重编,清理旧对象

确认是版本错位后,按当前内核重新清理、重新编译:

# 当前运行内核 uname -r # 清掉旧编译产物 make clean # 严格用当前内核的 build 目录重编 make -C /lib/modules/$(uname -r)/build M=$PWD modules # 对比模块 vermagic 和当前内核 modinfo ./e1000e.ko | grep -E 'vermagic|filename' uname -r

make clean要放在最前面,尤其是驱动目录从别的机器拷贝过来时。make -C的路径必须用$(uname -r)展开后的当前内核,不能写死。modinfo的vermagic和uname -r对比时,注意 vermagic 里除了版本号还可能带SMP、preempt等配置标记,这些标记也要和当前内核一致。如果vermagic一致但加载仍报符号版本错误,检查/lib/modules/$(uname -r)/build/Module.symvers是不是当前内核的,缺失就重装对应的kernel-devel包。

4.4 Secure Boot 签名报错:Required key not available

还有一种加载失败不是版本问题,而是签名问题:

modprobe: ERROR: could not insert 'e1000e': Required key not available

系统开启 Secure Boot 时,内核只加载有有效

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

返回列表