飞牛升级1.1.30将Linux内核也升级到了6.18.18-trim 之前通过DKMS一键编译驱动的方法已经失效了,因为新内核在老驱动可以说是完全不兼容,本教程将通过修改驱动C代码实现兼容,建议在AI辅助下进行操作!!如果能升级vGPU驱动建议优先选择升级,本教程不适合纯小白!!!
飞牛升级内核导致vgpu驱动加载错误解决方案 - 攻略分享 飞牛私有云论坛 fnOS
本文以 nvidia/535.230.02 和 6.18.18-trim 内核为例。实际操作时要注意修改为自己的驱动版本号
1. 先理解问题
NVIDIA 驱动不是普通应用,它需要编译出和当前内核匹配的内核模块,例如:
nvidia.ko
nvidia-uvm.ko
nvidia-modeset.ko
nvidia-drm.ko
nvidia-peermem.ko
内核升级后,旧驱动源码可能跟不上新内核接口变化,于是 DKMS 自动编译失败。常见表现是:
dkms build nvidia/535.230.02
输出类似:
Error! Bad return status for module build on kernel: 6.18.18-trim (x86_64)
Consult /var/lib/dkms/nvidia/535.230.02/build/make.log for more information.
这句话只表示“编译失败”,真正原因在 make.log 里。
2. 排查前准备
先切换到管理员权限。飞牛 NAS 默认不一定允许直接使用 root 登录,建议通过已有管理员账号使用 sudo。
sudo -i
确认当前内核:
kernel="$(uname -r)"
echo "$kernel"
查看 DKMS 状态:
dkms status
查看最近一次 NVIDIA DKMS 构建日志:
tail -n 160 /var/lib/dkms/nvidia/535.230.02/build/make.log
3. 第一类错误:找不到 NVIDIA 公共头文件
如果日志中出现:
fatal error: os-interface.h: No such file or directory
fatal error: nv-firmware.h: No such file or directory
fatal error: nv-dmabuf.h: No such file or directory
fatal error: nv-pci-types.h: No such file or directory
说明 NVIDIA 源码不是完全缺失,而是编译参数没有正确带上 common/inc include 路径。
先确认头文件确实存在:
find /usr/src/nvidia-535.230.02 -name os-interface.h -o -name nv-firmware.h
如果能看到类似:
/usr/src/nvidia-535.230.02/common/inc/os-interface.h
/usr/src/nvidia-535.230.02/common/inc/nv-firmware.h
就说明源码在,只是新内核 Kbuild 没有吃到旧驱动写在 EXTRA_CFLAGS 里的参数。
备份并修复:
src="/usr/src/nvidia-535.230.02/Kbuild"
cp -a "$src" "$src.bak-$(date +%Y%m%d-%H%M%S)"
if ! grep -q 'ccflags-y += $(EXTRA_CFLAGS)' "$src"; then
printf '\nccflags-y += $(EXTRA_CFLAGS)\n' >> "$src"
fi
4. 第二类错误:timer 接口变更
如果日志中出现:
error: implicit declaration of function 'del_timer_sync'
说明当前内核已经使用新接口 timer_delete_sync()。
确认内核头文件:
grep -n "timer_delete_sync" "/lib/modules/$(uname -r)/build/include/linux/timer.h"
如果存在,就给 NVIDIA Kbuild 添加兼容宏:
src="/usr/src/nvidia-535.230.02/Kbuild"
cp -a "$src" "$src.timer-bak-$(date +%Y%m%d-%H%M%S)"
if ! grep -q "del_timer_sync=timer_delete_sync" "$src"; then
cat >> "$src" <<'EOF'
ifneq ($(shell grep -q "timer_delete_sync" "$(NV_KERNEL_SOURCES)/include/linux/timer.h" 2>/dev/null && echo y),)
ccflags-y += -Ddel_timer_sync=timer_delete_sync
endif
EOF
fi
5. 第三类错误:DRM 接口变更
如果日志中出现这些错误:
fb_create from incompatible pointer type
struct drm_driver has no member named 'date'
mode_valid from incompatible pointer type
说明 DRM 子系统接口发生了变化:
fb_create 多了 const struct drm_format_info *info 参数
connector mode_valid 的 mode 参数变成了 const
struct drm_driver.date 字段被移除
这一步需要改 NVIDIA DRM 源码。建议只做条件兼容,不要把逻辑写死到某一个内核版本。
需要修改的文件通常是:
/usr/src/nvidia-535.230.02/Kbuild
/usr/src/nvidia-535.230.02/nvidia-drm/nvidia-drm-drv.c
/usr/src/nvidia-535.230.02/nvidia-drm/nvidia-drm-connector.c
/usr/src/nvidia-535.230.02/nvidia-drm/nvidia-drm-fb.c
/usr/src/nvidia-535.230.02/nvidia-drm/nvidia-drm-fb.h
建议每改一个文件都先备份:
file="/usr/src/nvidia-535.230.02/nvidia-drm/nvidia-drm-drv.c"
cp -a "$file" "$file.bak-$(date +%Y%m%d-%H%M%S)"
核心思路是:
- 在
Kbuild 中检测内核头文件是否包含新接口
- 根据检测结果定义宏
- 在 C 文件里用
#if defined(...) 同时兼容新旧内核
示例宏:
ifneq ($(shell grep -q "const struct drm_format_info \*info" "$(NV_KERNEL_SOURCES)/include/drm/drm_mode_config.h" 2>/dev/null && echo y),)
ccflags-y += -DNV_DRM_MODE_CONFIG_FUNCS_FB_CREATE_HAS_FORMAT_INFO_ARG
endif
ifneq ($(shell grep -q "const struct drm_display_mode \*mode" "$(NV_KERNEL_SOURCES)/include/drm/drm_modeset_helper_vtables.h" 2>/dev/null && echo y),)
ccflags-y += -DNV_DRM_CONNECTOR_MODE_VALID_HAS_CONST_MODE_ARG
endif
ifneq ($(shell grep -q "[[:space:]]date;" "$(NV_KERNEL_SOURCES)/include/drm/drm_drv.h" 2>/dev/null && echo y),)
ccflags-y += -DNV_DRM_DRIVER_HAS_DATE
endif
6. 第四类错误:GPL-only 符号
如果编译已经进入 MODPOST 阶段,但失败为:
ERROR: modpost: GPL-incompatible module nvidia.ko uses GPL-only symbol '__vma_start_write'
这说明 NVIDIA 535 调用了 vm_flags_set() / vm_flags_clear(),而新内核里这两个内联函数会间接引用 GPL-only 符号。闭源 NVIDIA 模块不能引用这个符号。
可以改 NVIDIA 自己的包装函数,让它优先使用 __vm_flags_mod()。
目标文件:
/usr/src/nvidia-535.230.02/common/inc/nv-mm.h
核心思路:
#if defined(NV___VM_FLAGS_MOD_PRESENT)
__vm_flags_mod(vma, flags, 0);
#else
vm_flags_set(vma, flags);
#endif
对应清理 flags 时:
#if defined(NV___VM_FLAGS_MOD_PRESENT)
__vm_flags_mod(vma, 0, flags);
#else
vm_flags_clear(vma, flags);
#endif
同时在 Kbuild 中检测:
ifneq ($(shell grep -q "static inline void __vm_flags_mod" "$(NV_KERNEL_SOURCES)/include/linux/mm.h" 2>/dev/null && echo y),)
ccflags-y += -DNV___VM_FLAGS_MOD_PRESENT
endif
7. 重新构建和安装 DKMS
构建:
dkms build nvidia/535.230.02
安装:
dkms install nvidia/535.230.02
查看状态:
dkms status
正常应看到:
nvidia/535.230.02, 6.18.18-trim, x86_64: installed
8. 注意模块版本冲突
飞牛系统可能自带另一套 NVIDIA 模块,例如 580.142。如果你的 GPU 只支持 535,而系统优先加载 580,就会出现:
NVRM: The NVIDIA GPU ... is not supported by the NVIDIA 580.142 driver release.
modprobe: ERROR: could not insert 'nvidia': No such device
确认当前 modprobe 会加载哪个模块:
modprobe --show-depends -S "$(uname -r)" nvidia | head -n 5
如果它指向:
/lib/modules/.../updates/trim/alternatives/nvidia-gpu/nvidia.ko
而不是:
/lib/modules/.../updates/dkms/nvidia.ko
可以添加 depmod 覆盖规则,让 DKMS 的 535 优先:
cat > /etc/depmod.d/nvidia-dkms-535.conf <<'EOF'
override nvidia * updates/dkms
override nvidia-uvm * updates/dkms
override nvidia-modeset * updates/dkms
override nvidia-drm * updates/dkms
override nvidia-peermem * updates/dkms
EOF
depmod -a "$(uname -r)"
再次确认:
modprobe --show-depends -S "$(uname -r)" nvidia | head -n 5
应该指向:
/lib/modules/当前内核/updates/dkms/nvidia.ko
9. 加载和验证
加载模块:
modprobe nvidia
modprobe nvidia-uvm
modprobe nvidia-modeset
modprobe nvidia-drm
查看模块是否加载:
lsmod | grep -E '^nvidia'
查看 GPU:
nvidia-smi
如果成功,会看到类似:
NVIDIA-SMI 535.230.02
Driver Version: 535.230.02
GPU Name
GRID P4-2Q
也可以用更适合脚本检查的方式:
nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader
10. 新手排障顺序总结
遇到 DKMS 失败时,不要先猜原因,按这个顺序查:
-
看当前内核:
uname -r
-
看 DKMS 状态:
dkms status
-
看真正错误日志:
tail -n 160 /var/lib/dkms/nvidia/535.230.02/build/make.log
-
每次只修复一个明确错误。
-
每次改源码前都备份。
-
构建成功后再安装。
-
安装后确认 modprobe 实际加载的是正确版本。
-
最后用 nvidia-smi 验证。
11. 本次处理结果参考
本次实际处理后的关键结果:
DKMS 状态:
nvidia/535.230.02, 6.18.18-trim, x86_64: installed
模块路径:
/lib/modules/6.18.18-trim/updates/dkms/nvidia.ko
GPU 查询:
GRID P4-2Q, 535.230.02, 2048 MiB
这说明:
- NVIDIA 535 DKMS 已经适配当前内核并安装成功
- 系统模块解析顺序已经切到 DKMS 版本
- 驱动能正常加载
nvidia-smi 能正常识别 GPU
12. 回滚建议
如果后续需要回滚,优先按备份文件恢复,而不是直接删除目录。
查看备份:
find /usr/src/nvidia-535.230.02 -name "*.bak-*" -o -name "*.sig-bak-*" -o -name "*.vmflags*-bak-*"
移除 depmod 覆盖规则:
rm -f /etc/depmod.d/nvidia-dkms-535.conf
depmod -a "$(uname -r)"
卸载 DKMS 模块:
dkms remove nvidia/535.230.02 --all
回滚前建议先确认业务是否依赖 GPU,避免影响正在运行的转码、虚拟化或容器任务。