多卡训练遇CUDA error 802?从驱动到PCIe的排查与修复全攻略
多卡机器上跑训练跑得好好的某天突然一个CUDA error: 802拍在脸上很多人的第一反应是代码写错了。其实 802 这个错误码CUDA_ERROR_SYSTEM_NOT_READY和代码的关系往往不大它说的是 CUDA 运行时的底层依赖——驱动、GPU 设备、驱动与内核模块之间的通信出了问题。尤其多卡机器情况比单卡复杂得多我今天就把自己在多台多卡服务器上排查这个错误的完整过程、原因分析、修复手段一次讲清楚希望能帮你少走些弯路。这个错误在单卡机器上偶尔也会出现但多卡机器上的触发频率和排查难度会明显高一个档次。它不挑显卡品牌也不挑 CUDA 版本无论是 3090、4090、A100 还是 V100 的机器我都遇到过。这篇文章会把 802 错误的核心原理、排查步骤、多卡专属坑位以及修复工具全部梳理出来适合正在被这个错误折磨的深度学习从业者、运维同学也适合想提前了解这类故障处理思路的开发者。1. 先搞清楚这个错误到底在说什么1.1 802错误的本质CUDA运行时与驱动失联CUDA_ERROR_SYSTEM_NOT_READY直译过来是系统未就绪。CUDA 的运行时库libcudart在调用 GPU 设备时需要经过一套完整的下发链条你的程序 - CUDA Driver API - NVIDIA 内核驱动模块nvidia.ko、nvidia_uvm.ko - 显卡硬件。这个链条上任何一个环节失灵最终表现都可能是 802。我在实际排查中发现802 错误有一个非常典型的时间点不是程序启动时立刻报而往往是跑了一段时间之后才突然中断然后后续所有 CUDA 调用全部失败。这很关键。如果程序一启动就报 802大概率是驱动加载有问题如果跑着跑着才报那多半是运行过程中某个节点把设备踢出去了。从底层机制来看CUDA Runtime API 在每次调用cudaMemcpy、cudaLaunchKernel、cudaDeviceSynchronize等操作时要先向 driver 查询设备状态。如果 driver 返回设备不可用运行时就会把这个状态映射成具体的错误码。802 正是这种映射的产物但它没有单一根因更像是一个综合故障信号。需要区分的是802 和常见的 719CUDA_ERROR_DEVICE_UNAVAILABLE、999CUDA_ERROR_UNKNOWN不一样。719 通常表示设备存在但当前被占用或不可用999 是未知内部错误而 802 明确指向设备还插在 PCIe 槽位上但系统层面已经无法访问它了。诊断思路上802 需要优先排查驱动内核模块状态、NVML 是否还能枚举到设备、以及操作系统是否已经将设备从 PCI 总线上摘除。1.2 为什么多卡机器上更容易触发多卡机器的 802 触发概率比单卡高很多这不是玄学有明确的物理和软件层面的原因。第一功耗和供电。多块 GPU 满载运行时整机功耗经常冲到 1500W 以上如果电源余量不足或供电线路老化瞬时掉压会导致某块卡直接掉线。掉线之后驱动层面看到的就是设备不见了。第二PCIe 链路质量。多卡通常需要插在多个 PCIe 插槽上配合拆分支架如 PCIe Switch、NVLink 桥接等链路拓扑比单卡复杂。任何一条链路的信号质量问题都可能让系统在运行中触发 PCIe AERAdvanced Error Reporting进而把设备标记为不可用。第三散热和温度。多卡机箱内部风道设计不好相邻卡之间的温差可能超过 15 度温度超过阈值后 GPU 会触发热保护但有时保护机制并不能让设备优雅降级而是直接假死。第四软件层面的资源竞争。多卡程序如果用 NCCL 做集合通信频繁的 kernel launch 和显存分配会让nvidia-uvm模块承受很大压力uvm 模块一旦出现锁异常或 DMA 映射失败也可能诱发设备的不可用状态。所以排查多卡机器上的 802不能只盯着驱动必须把硬件健康、电源、散热、PCIe 链路、驱动模块整体过一遍。下面我会按排查的先后顺序给出可落地的操作步骤。2. 收到802前的系统体检第一轮排查2.1 先看nvidia-smi是否还活着遇到 802 之后第一件事不是重装 CUDA而是先跑一个命令nvidia-smi这个命令能直接反映 NVMLNVIDIA Management Library和内核驱动的通信状态。我在排查时遇到过几种典型的输出正常输出能看到每张卡的型号、温度、显存占用、驱动版本。No devices were foundNVML 完全枚举不到 GPU这说明内核驱动加载失败或设备已经从 PCIe 总线上消失。Failed to initialize NVML: Driver/library version mismatch驱动和 NVML 库版本对不上。卡在被枚举到但其中一张卡显示ERR!或温度、功耗字段是N/A。如果nvidia-smi本身就报错那几乎可以断定 802 的根因在驱动层或硬件层和 CUDA 版本无关。这时候不要浪费时间在改代码上。紧接着再跑一条nvidia-smi -L这条命令会列出所有 GPU 的 UUID。如果某张卡在列表中缺失那么就是这张卡掉了。在多卡机器上802 错误通常会带设备序号比如CUDA error: 802 at device 3这个序号和nvidia-smi -L的枚举顺序是能对上的前提是没有设置CUDA_VISIBLE_DEVICES做重映射。我建议大家把这些输出重定向到文件里留档nvidia-smi -L gpu_list.txt 21 nvidia-smi nvidia_smi_status.txt 21为什么建议留档因为 802 经常是间歇性的第一次nvidia-smi可能正常过几分钟就报错了。留档可以对比不同时间点设备的枚举状态定位是否同一张卡反复掉线。2.2 查dmesg和系统日志里的NVRM报错如果nvidia-smi显示设备缺失或异常下一步就要看内核日志。Linux 下直接查 dmesgdmesg -T | grep -i -E nvrm|nvidia|pcie|aer|timeout | tail -100-T参数会把时间戳转成可读格式。我在多台机器上见过的高频报错包括NVRM: GPU at 0000:3b:00.0 has fallen off the bus NVRM: GPU 0000:3b:00.0 is already on the bus NVRM: Xid (PCI:3b:00): 79, GPU has fallen off the bus nvidia-nvlink: Unhandled error interrupt其中Xid 79是显卡掉总线的典型标志。出现这行日志基本就说明系统层面的 PCIe 通信已经中断。还要留意有没有NVRM: failed to copy user data、NVRM: os_schedule这类伴随日志它们能帮你判断是通信问题还是驱动内存管理问题。除了 dmesg还要看系统日志journalctl -k --since 1 hour ago | grep -i -E nvrm|nvidia|pcie|aer有些发行版把内核日志统一交给 journald 管理dmesg 里未必能翻到全部历史。两条命令配合使用能看到从 802 发生时刻往前推的系统状态变化。2.3 驱动版本与CUDA版本的匹配检查确认内核驱动模块状态后再看版本匹配。这一步容易被人忽略在多卡机器上尤其容易踩坑——因为很多时候服务器是多人共用的今天这个人装了个新 CUDA明天那个人升级了驱动环境早就乱了。检查当前加载的驱动版本cat /proc/driver/nvidia/version检查编译驱动时的内核版本和当前内核是否一致modinfo nvidia | grep ^version uname -r驱动版本和 CUDA 版本的兼容矩阵NVIDIA 官方文档里有但现场排查时更快的做法是直接跑一个测试程序用cudaGetDeviceProperties拿 CUDA 运行时版本和驱动版本nvidia-smi | head -20nvidia-smi右上角会显示CUDA Version这个值是当前驱动支持的最高 CUDA 版本不是已安装的 CUDA 版本。如果你编译程序用的 CUDA 版本比驱动支持的版本高虽然大部分情况下能跑但在某些边界操作上会出现异常。我见过不止一次因为驱动太老、而 CUDA toolkit 太新导致 802 的案例虽然 802 的直接触发原因是驱动崩溃但版本不一致是导火索。这里我一般建议多卡生产环境驱动版本和 CUDA 版本不要追新选稳定版本组合。比如 A100 机器我常用 470.xx 或 525.xx 驱动配 CUDA 11.4 或 12.04090 这类新卡则用 535.xx 或更新的驱动。版本选择不能只看能用还要看显存分配、NCCL 通信是否稳定。3. 针对性的修复流程与操作实录3.1 场景一驱动已被系统更新/升级破坏这个场景是最常见也最好修的。Linux 系统在自动更新内核后NVIDIA 驱动模块需要重新编译安装如果没做这步驱动模块和当前内核不匹配整个nvidia模块就是加载不进去的。复现路径通常是系统自动更新内核版本从 5.15.0-78 变成了 5.15.0-86。原来的 NVIDIA 驱动是通过 runfile 安装的对应的.ko文件还编译在旧内核路径下。重启后新内核加载不到 nvidia.ko或者加载的是旧内核的模块导致签名校验失败。应用调用 CUDA直接报 802。修复步骤并不复杂关键是别乱。我先确认模块是否能加载sudo modprobe nvidia lsmod | grep nvidia如果没有任何输出就说明模块没加载成功。接下来看错误sudo dmesg -T | grep -i nvidia | tail -30常见错误是Unknown symbol in module或Invalid module format。这类错误意味着模块版本与当前内核不兼容需要重新编译安装驱动。推荐做法是彻底卸载旧驱动后安装当前内核对应的版本。卸载命令取决于当初的安装方式。runfile 方式安装的找到安装包后执行sudo ./NVIDIA-Linux-x86_64-535.129.03.run --uninstallapt 或 dnf 安装的就用对应包管理器卸载。卸载完成后安装匹配的驱动chmod x NVIDIA-Linux-x86_64-535.129.03.run sudo ./NVIDIA-Linux-x86_64-535.129.03.run --no-cc-version-check--no-cc-version-check是跳过 gcc 版本检查因为在某些新系统上 gcc 版本高于驱动支持范围不加这个参数会安装失败。安装完必须重启重启后才能保证内核模块加载路径正确。注意不要在驱动加载失败的情况下先去重装 CUDA toolkit那是本末倒置。CUDA 只是一个用户态库驱动才是和硬件打交道的核心驱动起不来CUDA 装多少遍都没用。3.2 场景二多卡P2P通信导致的链路不稳定第二个高频场景是 P2PPeer-to-Peer通信导致的设备失联。NCCL 在多卡训练时需要用到 P2P 传输而 P2P 依赖 PCIe BAR 映射和 NVLink。如果系统在 P2P 初始化过程中出现 DMA 映射失败可能导致设备状态异常。从这个角度来说排查 802 时可以临时关闭 P2P 试试。NCCL 提供了环境变量控制export NCCL_P2P_DISABLE1不同 NCCL 版本的变量名略有差异新版还有NCCL_P2P_LEVELLOC这种更细粒度的控制。临时禁用 P2P 后重新跑训练如果不报 802 了问题就指向 P2P 链路。但注意禁用 P2P 会显著降低多卡通信效率训练速度可能慢 30% 以上。所以这只是定位手段不是长期方案。真正解决 P2P 链路问题要从硬件层面入手检查 NVLink 桥接器是否安装牢固。我见过有机器 NVLink 桥松了平时跑单卡没问题一上多卡通信就报错。检查 PCIe 插槽的带宽配置。有些主板在插满多卡时会自动降速到 PCIe 3.0 x8这种降速会让某些对带宽敏感的操作超时。更新主板 BIOS 到最新版本。多卡机器的 PCIe 拓扑由 BIOS 初始化老版本 BIOS 在 4 卡以上拓扑上常有不稳定问题。另外CUDA_VISIBLE_DEVICES的重映射也可能影响 P2P。假设物理卡的 P2P 拓扑不是完全互联的比如卡 0 和卡 3 不在同一个 PCIe Switch 下而程序里把它们设置为可见设备 0 和 1CUDA 会尝试建立 P2P 连接失败时可能报 802。排查时可以用nvidia-smi topo -m查看拓扑图nvidia-smi topo -m输出类似GPU0 GPU1 GPU2 GPU3 GPU0 X PIX PHB SYS GPU1 PIX X SYS SYS GPU2 PHB SYS X PIX GPU3 SYS SYS PIX XPIX表示在同一 PCIe Switch 下PHB表示同一 CPU 根节点SYS表示跨 CPU。如果某两张卡的 P2P 是SYS级别它们之间的直接 P2P 传输往往走 QPI/UPI 链路速度慢且容易出问题。理解这张拓扑图对定位多卡通信问题非常有帮助。3.3 场景三ECC与时钟异常触发的不可恢复错误在数据中心显卡A100、V100、A800 等上ECC 显存的错误如果积累到一定程度会触发不可恢复错误UEGPU 会自动进入降级状态或直接不可用。这种状态下 CUDA 调用报 802驱动日志里通常能看到Xid 48ECC 错误相关的记录。诊断方法nvidia-smi -q -d ECC查看每张卡的 ECC 错误计数。重点关注Aggregate Uncorrectable SRAM Errors和Aggregate Uncorrectable DRAM Errors这两项如果数值在持续增长说明硬件可能有问题。对于 ECC 错误我的处理建议是先清理错误计数确认是否是历史残留sudo nvidia-smi --ecc-config0如果清理后一段时间内错误计数又快速增长建议联系厂商进行硬件检测。ECC 错误如果每次都落在固定物理位置说明显存颗粒可能故障。此外还可以检查 GPU 时钟是否异常。某些情况下因为超频或 PLL 配置异常GPU 会出现时钟错误。查看当前时钟nvidia-smi -q -d CLOCK如果发现 GPU 的 SM 时钟或显存时钟异常比如远低于默认值尝试重置时钟sudo nvidia-smi -lgc 0 sudo nvidia-smi -rmc 0这两条命令把 GPU 和显存时钟恢复为默认策略。在数据中心卡上也可以用nvidia-smi -q -d SUPPORTED_CLOCKS查询支持的时钟范围然后手动指定一个保守的值来跑测试。3.4 场景四PCIe链路与Resizable BAR问题第四个高频场景是 PCIe 链路问题。多卡机器在长时间高负载运行后PCIe 链路可能因为信号完整性问题出现降速或链路抖动严重时会直接把设备摘除。诊断链路状态sudo lspci -vvv -s 3b:00.0 | grep -i -E LnkSta|LnkCap|DevStaLnkStaLink Status显示当前链路速度和宽度比如5GT/s x16。如果发现链路宽度从 x16 掉到 x8 或更窄说明链路不稳定。此时重新探测 PCIe 设备可能恢复echo 1 /sys/bus/pci/devices/0000:3b:00.0/remove sleep 3 echo 1 /sys/bus/pci/rescan但这只是临时恢复手段不是长久之计。真正解决要从硬件层面排查清理 PCIe 插槽和显卡金手指的氧化层。检查供电线缆是否插紧特别是 PCIe 辅助供电线。检查主板 BIOS 中 PCIe 链路速度设置必要时降为 PCIe 3.0 测试稳定性。另外一个经常被忽略的点是 Resizable BAR可调整 BAR 大小。新版驱动默认开启了 Resizable BAR如果主板 BIOS 中该功能配置不正确可能导致 GPU BAR 映射异常。排查方式sudo nvidia-bug-report.sh然后搜索生成的nvidia-bug-report.log.gz中的BAR1信息。如果 BAR1 地址或大小异常可以在 BIOS 中关闭 Resizable BAR 选项再测试。实测下来有些主板开启 Resizable BAR 后多卡系统会出现偶发的 802关闭后问题消失。这类问题通常出在 CPU 直连 PCIe 根端口和 PCIe Switch 混合拓扑的机器上。4. 多卡机器的特殊坑位远超单卡的麻烦4.1 卡间拓扑与P2P互连的稳定性多卡机器上跑分布式训练NCCL 的通信路径会直接影响设备稳定性。我遇到过一台 4 卡 A100 机器只要用NCCL_P2P_LEVELPXB就会在训练中期报 802改用NCCL_P2P_LEVELLOC就完全正常。后来发现这台机器虽然四张卡都在同一个 NUMA 节点但实际 PCIe Switch 拓扑并不支持全互联。这里的排查思路是先跑一下 NCCL 自带的连通性测试定位是哪两张卡的链路有问题cd /usr/local/nccl-tests ./build/all_reduce_perf -b 128M -e 128M -f 2 -g 4如果报错或超时再用cudaDeviceCanAccessPeer写一个小测试程序逐对检查 P2P 能力#include cstdio #include cuda_runtime.h int main() { int deviceCount; cudaGetDeviceCount(deviceCount); for (int i 0; i deviceCount; i) { for (int j 0; j deviceCount; j) { int canAccess 0; cudaDeviceCanAccessPeer(canAccess, i, j); printf(Device %d - Device %d: %s\n, i, j, canAccess ? P2P OK : P2P NO); } } return 0; }编译运行nvcc -o peer_test peer_test.cu ./peer_test输出结果结合nvidia-smi topo -m的拓扑图能非常清晰地看出哪些卡之间有物理链路限制。通过环境变量限制 NCCL 的 P2P 级别是解决此类问题最快速的手段。实战心得很多多卡机器的 802 并不是硬件坏了而是 NCCL 在初始化 P2P 时触发了某个设备驱动状态异常导致整台机器所有卡全部失联。这时候杀进程、清显存都没有用只能重置 GPU。4.2 MIG模式切换导致的状态残留如果你用 A100 或 A800 这类支持 MIGMulti-Instance GPU的卡MIG 模式的切换也是 802 的高发温床。MIG 模式下一张物理卡被切分成多个 GPU 实例每个实例有独立的内存和计算单元。但 MIG 切换时如果某个实例还被进程占用或者实例状态没被正确清理物理卡会进入一种半初始化状态。处理方式很直接先停掉所有占用 GPU 的进程nvidia-smi --query-compute-appspid,used_memory --formatcsv sudo kill -9 pid然后禁用 MIG 模式sudo nvidia-smi -mig 0强制重置 GPUsudo nvidia-smi --gpu-reset注意--gpu-reset在有些驱动版本上对 MIG 模式支持的卡无效需要先切回非 MIG 模式再重置。如果以上操作都无效那只能重启机器。MIG 模式切换引发的问题在重启后通常会自愈。4.3 多卡程序里显存分配导致的隐性失联还有一种情况不是驱动崩溃而是程序自身把多卡机器玩坏了。CUDA 在调用cudaMalloc失败后如果没有正确处理错误继续往下执行整个 context 会进入不可恢复状态。在这种状态下后续所有 CUDA 调用都可能返回 802。我见过有同事在代码里这么写cudaMalloc(d_ptr, size); // 没有检查返回值 kernelgrid, block(d_ptr); cudaDeviceSynchronize();如果cudaMalloc返回的其实是一个错误码比如cudaErrorMemoryAllocation而代码没有检查后续 kernel launch 和同步操作就会基于一个无效的指针执行最终驱动报错、整个设备状态异常。在多卡训练中显存碎片化问题会被放大所以显存分配失败的几率也更高。这类问题的解法是代码规范层面的所有 CUDA Runtime API 调用都要检查返回值。cudaDeviceSynchronize之后要单独检查错误。用cudaMemGetInfo在分配前查看剩余显存提前规避分配失败。更有效的做法是给 CUDA 调用封装一个宏#define CUDA_CHECK(call) \ do { \ cudaError_t err call; \ if (err ! cudaSuccess) { \ fprintf(stderr, CUDA error at %s:%d code%d(%s)\n, \ __FILE__, __LINE__, err, cudaGetErrorString(err)); \ exit(EXIT_FAILURE); \ } \ } while (0)这样出错时能立刻看到是哪一行代码触发的而不是等驱动崩了才去翻日志。我强烈建议所有多卡训练代码都加上这样的检查。5. 常见问题速查表与避坑清单5.1 常见问题速查表下面这个表是我在多台机器上排查 802 的实践经验汇总。遇到问题时可以按这个表快速定位方向。现象可能原因优先排查手段快速修复nvidia-smi报 Driver/library version mismatch驱动更新不完整或系统自动更新破坏了模块modinfo nvidiauname -r重装对应内核版本的驱动nvidia-smi报 No devices found内核模块加载失败或设备掉总线dmesg查 NVRM/Xid 日志modprobe nvidia必要时重启训练中途报 802nvidia-smi能看到卡P2P/NVLink 通信异常nvidia-smi topo -m NCCL 测试设置NCCL_P2P_DISABLE1验证某张卡温度/功耗显示 N/A设备进入保护或掉电检查散热和供电关机后重新插拔供电线多卡通信时报 Xid 79GPU 掉总线PCIe 链路不稳lspci -vvv看链路状态重新插拔 PCIe 设备ECC 错误计数持续增长显存颗粒损坏nvidia-smi -q -d ECC清零后观察增长趋势MIG 模式下卡状态异常MIG 状态残留nvidia-smi -mig 0重启机器5.2 我在多卡机器上踩过的坑与经验总结第一个常犯的错误一上来就重装驱动。802 的根因可能只是电源供电不稳重装驱动毫无作用反而浪费时间。我建议在重装驱动之前先花 10 分钟做上面说的体检流程至少把nvidia-smi的输出、dmesg的 NVRM 日志、nvidia-smi topo -m的拓扑图截下来这些信息在后续排查时非常有用。第二个容易忽略的点多卡机器上的 Xid 日志不只是记录在 dmesg 里。如果你装了 NVIDIA 的 data center GPU managerDCGM它会把错误事件记录到/var/log/dcgm/下。另外有些容器环境会把宿主机的日志屏蔽你需要在宿主机上查看而不是在容器里查。这一点在做容器化部署时尤其容易坑人。第三个经验在服务重启之前先把所有 CUDA 进程杀干净。多卡机器上经常有多个用户同时跑任务某个用户的任务占着显存你重启驱动是不行的必须先 kill 所有相关进程。查占用进程用fuser -v /dev/nvidia*这条命令会列出所有打开 NVIDIA 设备文件的进程然后按 PID 逐个处理。但要注意多人共用机器时不要贸然 kill 别人的进程先沟通确认。第四个经验也是最想强调的做好规律的健康检查。多卡机器不是装好就能一直稳定跑的。我建议每隔一段时间就手动或定时跑一次nvidia-smi --query-gpuindex,utilization.gpu,memory.used,temperature.gpu,power.draw --formatcsv把输出存到日志文件里观察趋势。如果某张卡的功耗、温度、利用率明显异常早点处理别等 802 出来了才去抢救。5.3 最后的兜底方案驱动重装与系统重启如果上面所有排查手段都没能解决问题那就只能上兜底方案了。我个人的处理顺序是先reboot。多卡机器的 802 有很多是运行中瞬时故障引发的重启能清掉所有残留状态。实测下来大约三成的 802 重启就好不需要额外操作。重启后如果仍然复现 802再做驱动完整重装。卸载干净后安装与内核匹配的驱动版本。驱动重装后仍然有问题这时候基本可以判定是硬件层面的问题了需要逐卡排查。关掉机器把卡从原来的 PCIe 插槽换到另一个插槽或者在另一台机器上测试这张卡。兜底方案不只是最后手段也是排除法的一部分。我记得第一次处理 802 时就是依赖重启和换插槽才定位到一张 RTX 3090 的金手指氧化导致的问题。换了一个插槽后整整一个月没有复发。5.4 关于CUDA版本和驱动的长期稳定组合最后总结一下我自己的多卡环境稳定组合方案。目前主力生产环境用的是Ubuntu 22.04 LTS内核 5.15.0-91驱动 535.183.06 或 525.147.05CUDA 12.2配合用户态的 PyTorch 2.3 等这套组合在 4090、A100、L40S 上都验证过稳定性很好。如果你用的是 A100 且对代码兼容性要求高CUDA 11.8 驱动 525.147.05 也是稳妥的选择。另外一个很多人不知道的做法锁定内核版本禁止自动更新。多卡服务器最怕的就是系统自动更新内核一旦更新驱动模块大概率失效。Ubuntu 下可以用apt-mark hold linux-image-...锁定当前内核版本CentOS/RHEL 下则在/etc/yum.conf里加上excludekernel*。这一步可以提前避免非常多的麻烦。排查 802 的过程本质上是一个依赖链的检查过程从硬件供电、散热、PCIe 链路到内核模块、驱动版本、CUDA 运行时再到应用代码一层一层排除多数问题都能找到明确的根因。我个人最大的体会是不要一开始就怀疑 CUDA 版本也不要一上来就重装驱动。先看清楚错误发生时系统的真实状态再动手操作效率会高很多。每台多卡机器的硬件拓扑、供电条件、使用场景各不相同别人的解法未必直接适用但排查思路是相通的。希望这篇文章能帮你快速找到问题所在把时间省下来真正用在训练和调参上。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →