环境背景:一台连续运行 25 天的 3×RTX 4090 计算服务器(Ubuntu 22.04,NVIDIA 驱动 595.71.05,CUDA 13.2)。执行 nvidia-smi 时,三张 GPU 的风扇、温度、功耗读数全部显示 ERR!,GPU 不可用。本文完整记录故障的排查、定位、在线恢复与后续加固过程。


最重要的话说在前面:不要重启机器!不要重启机器!不要重启机器!

不然你会得到一张返厂维修单。。。

故障现象

三张 GPU 的 nvidia-smi 输出如下:

1
2
3
4
5
6
7
8
9
10
11
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 4090 On | 00000000:01:00.0 N/A | N/A |
|ERR! ERR! ERR! N/A / N/A | 1MiB / 24564MiB | N/A Default |
| 1 NVIDIA GeForce RTX 4090 On | 00000000:41:00.0 N/A | N/A |
|ERR! ERR! ERR! N/A / N/A | 1MiB / 24564MiB | N/A Default |
| 2 NVIDIA GeForce RTX 4090 On | 00000000:61:00.0 N/A | N/A |
|ERR! ERR! ERR! N/A / N/A | 1MiB / 24564MiB | N/A Default |
+-----------------------------------------+------------------------+----------------------+

通过 nvidia-smi -q 查询详情,关键状态字段为:

1
2
Product Brand     : GPU requires reset
GPU Recovery Action : Reset

即三张 GPU 均处于挂起状态,必须执行 reset 方可恢复。

根因分析:GSP 固件空指针崩溃

查阅内核日志(journalctl -k 与 /var/log/kern.log),定位到 18 小时前(09-20 22:42:49)的原始事件:

1
2
3
4
5
NVRM: GPU1 kgspHealthCheck_TU102: ********* GSP-CrashCat Report *********
NVRM: Xid (PCI:0000:41:00): 120, pid=3714926, name=Xorg,
GSP task exception: load access page fault (cause:0xd) @ pc:0x138f064
NVRM: Xid (PCI:0000:41:00): 154, GPU recovery action changed from
0x0 (None) to 0x1 (GPU Reset Required)

理解该故障需要先明确几个概念:

名词 说明
GSP RTX 4090(Ada 架构)板载的一颗 RISC-V 微控制器。新版 NVIDIA 驱动将资源管理器(显存分配、对象管理、显示配置等)从主机内核迁移至 GSP 固件中运行,称为 GSP-RM
Xid 120 GSP 固件自身抛出异常。本次为 load access page fault,即读取内存时发生页错误
Xid 154 驱动将 GPU 的恢复动作置为 Reset Required,即只能通过复位恢复

GSP-CrashCat 报告给出了崩溃现场,关键字段:

1
sepc:0x000000000138f064   stval:0x00000000000001c0   scause:0xd

scause=0xd(load access page fault)配合 stval=0x1c0,表明固件在执行读指令时访问了地址 0x1c0——这是一个极小的地址,对应空指针的 0x1c0 偏移解引用,属于典型的固件代码缺陷。

三张 GPU 在同一秒内崩溃的原因在于:三张卡的崩溃现场完全一致——相同的 PC(0x138f064)、相同的故障地址(0x1c0)、几乎相同的寄存器状态。硬件不可能以完全相同的方式同时损坏,因此可以判定为确定性的固件代码缺陷:同一客户端在约 100ms 内向三张 GPU 发送了相同的一组请求,每张卡的 GSP 固件均执行到了同一处缺陷代码。

触发路径:显示器信号事件与 Xorg

GSP 固件缺陷是根因,但仍需解释为何该代码路径在此刻被触发。结合崩溃瞬间正在处理的 RPC 序列与 Xorg 日志,可以还原完整链条:

  1. 该机器采用 3 头 X 显示配置(gdm 登录界面,一个 Xorg 进程同时驱动三张 4090,连接 Dell 显示器);
  2. 崩溃前最后处理的 RPC 序列为:两组 0x2080a0a8(NV2080 对象中 0xA0xx 段属于显示命令块)控制调用,随后是一条正在处理的 FREE(对象释放) 请求;
  3. Xorg 日志中崩溃前最后一批正常输出为 EDID 读取与 Modeline 转储,属于显示器重探测行为(DP 链路信号重训、热插拔或显示器待机唤醒等事件);
  4. GSP 崩溃后,X 驱动的首个报错为 Failed to allocate display software resources。

完整因果链:显示器信号事件 → X server 重探测 EDID → 显示对象批量拆解与重建(FREE 请求)→ GSP 固件在对象释放路径上发生空指针解引用(Xid 120)→ GSP 作为 GPU 资源管理器失效,GPU 停止响应(Xid 154)→ 三张 GPU 于同一秒内全部进入 Reset Required 状态。

Xorg 与显示器仅为触发条件,本身无过错。对于不连接显示器、不运行图形栈的纯计算环境,该代码路径不会被触及,此类故障不会发生。

在线恢复:带内复位失效,改用 PCI FLR

GPU 挂死后的标准恢复手段为 nvidia-smi --gpu-reset,但执行后返回:

1
2
The following GPUs could not be reset:
GPU 00000000:01:00.0: Not Supported

原因:带内(in-band)复位依赖 GPU 侧 GSP 配合响应,而 GSP 已经崩溃,该路径不可用。

需改用硬件级手段:PCI FLR(Function Level Reset)。FLR 是 PCIe 规范定义的硬件复位机制,不依赖 GPU 固件。操作前先确认拓扑:三张 GPU 各独占一条 PCIe 总线(01、41、61),总线上仅有对应 GPU 的音频功能设备,复位不影响其他设备。完整流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 释放 GPU 上的全部进程(gdm/Xorg、nvidia-persistenced 及监控进程)
sudo systemctl stop gdm3
sudo systemctl stop nvidia-persistenced

# 2. 卸载显示相关模块,将 GPU 从 nvidia 驱动解绑
sudo rmmod nvidia_uvm nvidia_drm nvidia_modeset
for bdf in 0000:01:00.0 0000:41:00.0 0000:61:00.0; do
echo $bdf | sudo tee /sys/bus/pci/devices/$bdf/driver/unbind
done

# 3. 执行 FLR 硬件复位
for bdf in 0000:01:00.0 0000:41:00.0 0000:61:00.0; do
echo 1 | sudo tee /sys/bus/pci/devices/$bdf/reset
done

# 4. 重新绑定驱动,GSP 固件随 probe 流程重新初始化
for bdf in 0000:01:00.0 0000:41:00.0 0000:61:00.0; do
echo $bdf | sudo tee /sys/bus/pci/drivers/nvidia/bind
done

三张 GPU 复位成功,驱动重新绑定后 GSP 固件重新加载,nvidia-smi 恢复正常:

1
2
|   0  NVIDIA GeForce RTX 4090        On |   00000000:01:00.0 N/A |
| 30% 26C P0 57W / 450W | 1MiB / 24564MiB | 0%

恢复过程未重启系统,25 天的连续运行时间得以保留。

后续加固措施

措施一:关闭 GSP 并持久化(可能有性能损失)

GSP 固件缺陷仍然存在,复发风险未消除。查阅驱动源码(/usr/src/nvidia-*/common/inc/nv-firmware-registry.h)确认参数语义后,在 /etc/modprobe.d/nvidia.conf 中写入:

1
options nvidia NVreg_EnableGpuFirmware=0

该参数低 4 位为模式选择:0=禁用、1=启用、2=默认策略。故障时运行值为 18(0x12,默认策略加回退位),即 4090 默认启用 GSP。设为 0 后,资源管理回退至主机侧 RM——NVIDIA 长期维护的成熟代码路径,可彻底绕开出事的固件代码。(注:该配置在驱动升级后撤销,见下文。)

措施二:显示输出迁移至板载显卡

机器配备 ASPEED BMC 板载 VGA(63:00.0,内核 ast 驱动,系统主帧缓冲 fb0)。目标:桌面会话运行于板载显卡,4090 不再承载任何显示进程。实施步骤:

  1. 编写 /etc/X11/xorg.conf,将 X server 固定绑定至板载显卡:
1
2
3
4
5
6
7
8
9
Section "Device"
Identifier "onboard-ast"
Driver "modesetting"
EndSection

Section "Screen"
Identifier "onboard-screen"
Device "onboard-ast"
EndSection
  1. 在 /etc/modprobe.d/nvidia.conf 中屏蔽 nvidia 的 KMS 模块,使 4090 不具备显示接口:
1
blacklist nvidia_drm
  1. 处理两个预期外问题:
    • udev 规则绕过黑名单:/lib/udev/rules.d/71-nvidia.rules 中存在 RUN+="/sbin/modprobe nvidia-drm",nvidia 驱动绑定时会显式加载 nvidia_drm,modprobe 黑名单无法拦截显式请求。在 /etc/udev/rules.d/ 下覆盖该规则并删除对应行后解决;
    • 远程桌面软件占用:ToDesk 服务进程持有三张 4090 的 DRM 设备句柄,重启该服务后释放。

措施三:驱动版本升级(595.71.05 → 595.91.07)

595 分支存在安全更新版本 595.91.07,同步完成升级。过程中遇到两个问题:

  • 驱动相关包被 dpkg hold 锁定,需先 apt-mark unhold 解除;
  • Ubuntu 驱动包存在命名迁移(如 libnvidia-cfg1 → libnvidia-cfg1-595),新旧包声明了同一文件 libnvidia-cfg.so.1,dpkg 解包时报文件冲突。先 dpkg --remove 移除旧包,再执行 apt -f install 修复完成。

DKMS 编译新内核模块后,在不重启系统的前提下完成模块热替换(复用前述解绑/绑定流程):

1
2
3
4
5
6
7
8
for bdf in 0000:01:00.0 0000:41:00.0 0000:61:00.0; do
echo $bdf | sudo tee /sys/bus/pci/devices/$bdf/driver/unbind
done
sudo rmmod nvidia_uvm nvidia_modeset nvidia
sudo modprobe nvidia nvidia_uvm
for bdf in 0000:01:00.0 0000:41:00.0 0000:61:00.0; do
echo $bdf | sudo tee /sys/bus/pci/drivers/nvidia/bind
done

热替换完成,三张 GPU 的 driver_version 均为 595.91.07。鉴于新版本的 GSP 固件可能已修复本次 Xid 120 缺陷,撤销了 GSP 关闭配置(GSP 恢复默认启用状态),转入观察期。

衍生问题:nvidia-smi 查询延迟增大

升级完成后发现 nvidia-smi 响应时间从秒级增大至约 3 秒。通过分步定位确认瓶颈所在:

  • 最小查询(--query-gpu=index)同样耗时 2.9s,排除特定查询字段的问题,瓶颈位于 NVML 初始化(设备 attach) 阶段;
  • 后台维持一个常驻 NVML 连接(nvidia-smi dmon)后再次测量,耗时降至 0.04s。

结论:每次新建 NVML 连接需支付约 3 秒的 GSP 唤醒握手开销。根本原因是持久模式(persistence mode)处于关闭状态——595.91 版本的 nvidia-persistenced.service 厂商单元文件中带有 --no-persistence-mode 参数。无客户端连接时 GSP 进入低功耗挂起状态,而 nvidia-smi 每次执行均为新进程、新连接,因此每次都需要唤醒 GSP。

此前未暴露该问题,是因为 4090 上长期存在 Xorg、ToDesk 等常驻连接,GPU 未进入完全空闲状态;显示输出迁移至板载显卡后,GPU 进入真正空闲,该问题随之显现。

修复方案:

1
sudo nvidia-smi -pm 1    # 立即生效

为使其在重启后依然有效,通过 systemd drop-in 移除 --no-persistence-mode 参数(/etc/systemd/system/nvidia-persistenced.service.d/override.conf),由守护进程在启动时为全部 GPU 开启持久模式。

修复后实测:GPU 空闲 60 秒后执行 nvidia-smi,耗时 0.09s(修复前 2.9-3.2s)。

总结与经验

# 经验
1 GSP 是新版 NVIDIA 驱动架构中的单点:资源管理集中于板载固件,固件崩溃将导致整卡不可用。GeForce 显卡支持通过 NVreg_EnableGpuFirmware=0 关闭 GSP,计算集群应评估该配置的稳定性收益
2 多卡同时故障且崩溃现场完全一致(PC、故障地址、寄存器状态)时,应优先判定为软件缺陷而非硬件损坏。GSP-CrashCat 报告是定位此类问题的关键材料
3 GSP 失效后带内复位(nvidia-smi --gpu-reset)不可用,返回 Not Supported。此时应走 unbind → /sys/.../reset(FLR)→ bind 的纯硬件复位路径
4 通过 PCI 设备的解绑/绑定操作,可在不重启系统的前提下完成 GPU 复位与内核驱动版本热替换,是保障在线服务可用性的重要手段
5 modprobe 黑名单无法拦截 udev 规则中的显式 modprobe 调用,排查模块加载来源时需同时检查 /lib/udev/rules.d/ 下的驱动规则
6 持久模式是低延迟访问的前提:关闭状态下 GSP 会进入挂起,每次新连接产生秒级唤醒延迟。计算服务器建议常驻开启(nvidia-smi -pm 1)
7 驱动升级后用户态库与内核模块版本必须一致,否则 NVML 报 Driver/library version mismatch,需重启系统或热替换内核模块
8 向 NVIDIA 报告 GSP 缺陷时,完整的 GSP-CrashCat 报告(含 RISC-V 寄存器状态、堆栈、GSP bin buildId)与触发时的 RPC 历史是必要的复现材料