Hotdry.
systems

NVIDIA GPU 驱动 66 天看门狗超时:nvidia-smi 永久挂起的根因与工程缓解

分析 NVIDIA GPU 驱动在约 66 天运行时出现的 nvidia-smi 永久挂起问题,探讨内核看门狗超时、UVM 上下文持有与设备文件锁竞争的根因,并给出 persistence mode、周期性探活与监控阈值等工程缓解措施。

在长时间运行的 GPU 计算节点上,一个隐蔽但影响严重的问题正在累积:执行 nvidia-smi 命令时,进程会永久阻塞,所有后续查询均无响应,即使发送 SIGKILL 也无法终止。这一现象并非偶发的硬件故障,而是一个与 Linux 内核计时器、NVIDIA 驱动内部状态管理密切相关的系统性缺陷。本文将深入剖析其根因机制,并提供可落地的工程缓解方案与监控建议。

现象描述:66 天的运行魔咒

根据 GitHub 上的多份报告,配备 NVIDIA B200 或类似高端 GPU 的服务器在连续运行约 66 天后,会出现 nvidia-smi 命令永久挂起的症状。用户在尝试查询 GPU 状态、驱动版本或功耗信息时,命令行完全卡死,没有任何输出或错误提示。即使在另一个终端尝试 kill -9 发送强制终止信号,挂起的 nvidia-smi 进程依然保持 D 状态(uninterruptible sleep),这表明进程正在等待某个永远不会完成的 I/O 操作或内核调用。

从 dmesg 日志中可以观察到一组特征性的 NVRM 错误信息,这些错误与 NvLink 链路的状态检测相关。内核日志中反复出现 knvlinkUpdatePostRxDetectLinkMask_IMPL: Failed to update Rx Detect Link mask!knvlinkDiscoverPostRxDetLinks_GH100: Getting peer's postRxDetLinkMask failed! 的告警,说明驱动在与 GPU 硬件的通信过程中遇到了协议层面的异常。然而,这些 NvLink 错误究竟是挂起的原因还是挂起后的伴随症状,目前尚无定论。NVIDIA 官方已将该问题标记为 NV-Bug 进行调查,但从报告到修复的周期往往以月计,生产环境无法被动等待。

值得注意的关键细节是,这一问题在使用开源 GPU 内核模块(OpenRM)的环境中被明确复现,而在闭源驱动包中是否同样存在,目前证据尚不充分。此外,受影响的系统运行的内核版本集中在 6.6.x 系列,操作系统包括 Ubuntu、OpenEuler 等主流发行版,硬件平台覆盖数据中心级 GPU 与消费级显卡。这表明问题很可能位于 NVIDIA 驱动内核模块的核心路径中,而非特定于某一代架构的硬件缺陷。

根因分析:计时器溢出与看门狗机制

深入分析 66 天这一时间阈值,可以发现其与 Linux 内核的 jiffies 计时器存在潜在关联。jiffies 是一个记录系统启动以来定时器中断次数的全局变量,在 32 位系统上以 32 位无符号整数存储。当 HZ 配置为 1000(每秒 1000 次中断)时,2 的 32 次方次 tick 对应的时间跨度约为 49.7 天。66 天虽然略高于这一数值,但考虑到 GPU 驱动内部可能使用更高精度的计时器(如毫秒级或微秒级),或者在特定负载条件下计时器累积速度加快,实际触发窗口落在 50 至 70 天范围内是合理的。

Linux 内核的软锁检测机制(soft lockup detector)依赖于 jiffies 或其 64 位变体 jiffies_64 来判断某个 CPU 核心是否长时间未响应调度器。当驱动代码进入无限循环或长时间持有自旋锁(spinlock)时,看门狗线程会触发 "BUG: soft lockup" 或 "NMI watchdog: BUG: soft lockup" 错误。然而,nvidia-smi 挂起案例中并未出现标准的软锁提示,这意味着问题更可能出在 I/O 等待路径上,而非计算密集型死循环。

另一个关键假设涉及 UVM(Unified Virtual Memory)上下文的生命周期管理。NVIDIA 驱动为每个使用 CUDA 或设备映射的进程维护一个 GPU 上下文对象,这些上下文通过内部引用计数管理内存与 I/O 资源的分配与释放。如果某个上下文在长时间运行后因计时器溢出导致内部时间戳比较失效,可能引发资源泄漏或状态不一致,最终导致后续所有针对该 GPU 的 ioctl 调用在等待资源释放时永久阻塞。

设备文件锁竞争也是不可忽视的因素。/dev/nvidiactl/dev/nvidia* 是用户空间与 GPU 驱动交互的核心接口,所有 nvidia-smi 查询最终都会转化为对这些设备文件的 ioctl 请求。当一个持有设备文件锁的线程因上述原因永久阻塞时,其他试图获取同一锁的线程也会被级联阻塞,形成典型的死锁场景。社区报告中提到的 "nvidia-smi 进程无法被 SIGKILL 杀死" 正是这一机制的直接后果:进程处于内核态的不可中断等待中,用户信号无法干预。

工程缓解:多层次的防御策略

在 NVIDIA 官方修复发布之前,运维团队可以采取一系列工程措施来降低该问题对生产环境的影响。这些策略可分为预防性配置、主动探活与快速恢复三大类,每类措施对应不同的成本与收益权衡。

持久化模式与电源管理调整

社区中经过验证的最有效临时解法是启用 NVIDIA 持久化模式(Persistence Mode),通过 nvidia-smi -pm 1 命令激活。这一选项会阻止 GPU 在空闲时进入低功耗状态,从而避免驱动在状态切换过程中可能出现的竞态条件。持久化模式的核心作用是保持 GPU 设备文件始终处于活跃状态,减少 nvidia-persistenced 服务与驱动之间的握手延迟。从实际效果来看,启用该选项后,nvidia-smi 的响应时间从不可预期的数十秒稳定到毫秒级。

对于使用 systemd 管理 GPU 服务的环境,建议在 nvidia-persistenced.service 的启动参数中显式添加 --persistence-mode,确保服务重启后配置不丢失。同时,需要检查系统的 ACPI 电源管理策略,确认 PCI Express ASPM(Active State Power Management)未在 GPU 链路上强制启用,以避免硬件层面的状态跳变触发驱动异常。

周期性探活与自动重启

对于无法立即启用持久化模式或问题依旧存在的场景,部署周期性的 nvidia-smi 探活任务是最直接的工程缓解手段。可以通过 cron 或 systemd timer 每小时执行一次 nvidia-smi --query-gpu=timestamp --format=csv,noheader,仅获取时间戳而不触发完整的状态查询。如果命令在预设的超时时间(如 5 秒)内未返回,则判定为挂起状态,触发自动恢复流程。

自动恢复流程应设计为渐进式:首先尝试通过 SIGTERM 或 SIGKILL 终止残留的 nvidia-smi 进程,如果失败则重启 nvidia-persistenced 服务,最后的手段是触发 GPU 复位(通过 NVML API 的 nvmlDeviceSetComputeMode 或系统级的 PCI-reset)。对于多 GPU 节点,建议按设备编号逐一处理,避免全局复位导致的不必要业务中断。监控告警应区分探活失败与恢复成功两个阶段,便于运维人员追踪问题发生频率与恢复效果。

运行时长阈值与滚动重启

考虑到问题在约 60 天后显著出现,可以将系统运行时长作为滚动重启策略的核心指标。对于需要 99.9% 以上可用性的 GPU 集群,建议将最大连续运行时间设置为 45 天或 50 天,通过自动化流水线在达到阈值前发起计划内重启。这一策略的成本是定期的短暂服务中断,收益是几乎可以完全规避挂起问题,特别适合可水平扩展的计算任务。

实施滚动重启时,需要协调 GPU 调度器(如 Slurm、Job scheduler)与集群管理平台,确保重启前任务已完成或正常迁移。对于运行中的长时间训练任务,可以在任务启动时记录系统 uptime,并在任务脚本中嵌入检查逻辑,在接近阈值时主动发起 checkpoint 保存与任务重调度。这种细粒度的管理需要在基础设施层面投入开发资源,但长期来看可以显著降低运营风险。

监控与可观测性建设

有效的监控体系是提前发现问题、评估缓解措施效果的关键。建议从以下维度构建可观测性栈:

进程状态监控应覆盖所有 nvidia-smi 与 nvidia-persistenced 进程的生存周期,通过 procfs 或 cgroup 获取进程状态码,D 状态进程的数量一旦超过零就应触发告警。内核日志中的 NVRM 错误频率同样需要纳入监控,特别是 NvLink 相关错误的大量出现往往是挂起前兆,建议配置基于日志流量的阈值告警。

GPU 健康指标的采集应绕过可能阻塞的 nvidia-smi 路径,转而使用 NVML 的 C API 或 Python binding 直接查询驱动内部状态。对于数据中心级 GPU,NVML 提供了 nvmlDeviceGetAccountingStats 等接口,可以获取自上次驱动加载以来的累计错误计数与运行时间,这些数据存储在驱动内部,不会因 nvidia-smi 挂起而不可访问。

设备文件锁状态可以通过 lsof 或 /proc 文件系统追踪,监控每个 nvidia 设备文件上的打开句柄数量与持有进程。如果某个设备文件的句柄长时间集中在单一进程,且该进程处于 D 状态,则基本可以判定为锁竞争导致的挂起。这一信号可以与上层的探活失败告警联动,提供更精确的根因定位。

根本修复预期与调试方法

从内核驱动的角度看,彻底修复该问题需要对以下代码路径进行审查与加固。首先是计时器相关的比较逻辑,所有使用 jiffies 或类似自增计数器进行超时判断的代码,都应改用内核提供的安全比较宏(如 time_aftertime_before),或在 64 位系统上直接使用 jiffies_64 以避免溢出。其次是 UVM 上下文的状态机,需要增加超时检测与异常恢复机制,防止单个上下文异常导致全局阻塞。最后是设备文件锁的实现,建议引入死锁检测或超时释放逻辑,确保即使持有锁的线程永久阻塞,锁本身也能在合理时间后被强制释放。

对于希望自行调试的开发者,可以在内核配置中启用 NMI watchdog(CONFIG_NMI_WATCHDOG=y)与软锁检测(CONFIG_LOCKUP_DETECTOR=y),在问题复现时捕获详细的调用栈信息。此外,通过 ftraceperf 追踪 nvidia 驱动模块的 ioctl 调用,可以定位阻塞发生的确切内核函数。NVIDIA 官方也可能需要内核开发者配合,在用户态触发 core dump 并使用 crash 工具分析内核态的进程状态与内存布局。

结语

nvidia-smi 在长运行系统上的永久挂起是一个典型的驱动与内核时序相关缺陷,其隐蔽性与影响范围使其成为 GPU 计算基础设施的潜在风险点。通过启用持久化模式、部署周期性探活、实施运行时长阈值管理等工程措施,可以在官方修复发布前有效降低业务中断风险。同时,建立完善的可观测性体系,积累挂起前兆的监控数据,有助于在问题规模化前采取预防性行动。对于追求稳定性的生产环境,建议将 66 天阈值纳入容量规划与运维 SOP,定期评估缓解措施的有效性,并在条件允许时升级至包含修复的驱动版本。

资料来源

  • GitHub NVIDIA/open-gpu-kernel-modules Issue #971
  • Stack Overflow: "nvidia-smi process hangs and can't be killed with SIGKILL either"
  • Oracle Linux Blog: "Jiffies: The Heartbeat of the Linux Operating System"
查看归档