Featured image of post NVIDIA GPU 驱动持久化

NVIDIA GPU 驱动持久化

记录一次故障排查过程,介绍 NVIDIA GPU 驱动持久化

NVIDIA GPU 驱动持久化配置与故障排查

概述

本文记录了一次由 GPU 驱动非持久化模式引发的监控异常问题,并介绍了 NVIDIA GPU 驱动持久化的原理与配置方法。


问题现象

在进行算法程序压测时,通过 Grafana 监控面板发现 Nvidia Exporter 服务运行不稳定,呈现时好时坏的状态:

Nvidia Exporter Service

初步排查

排除 Prometheus Scrape 问题
在目标 GPU 服务器上手动执行 curl http://localhost:9835/metrics 命令,请求陷入超时状态,确认问题出在 Exporter 服务本身。

调整日志级别
将 Nvidia Exporter 的日志等级调整为 debug,但未发现明显的错误信息。

定位根因
手动执行 Nvidia Exporter 内部使用的查询命令:

1
nvidia-smi --query-gpu=timestamp,driver_version,vgpu_driver_capability.heterogenous_multivGPU,count,name,serial,uuid,pci.bus_id,pci.domain,pci.bus,pci.device,pci.baseClass,pci.subClass,pci.device_id,pci.sub_device_id,vgpu_device_capability.fractional_multiVgpu,vgpu_device_capability.heterogeneous_timeSlice_profile,vgpu_device_capability.heterogeneous_timeSlice_sizes,vgpu_device_capability.homogeneous_placements,pcie.link.gen.current,pcie.link.gen.gpucurrent,pcie.link.gen.max,pcie.link.gen.gpumax,pcie.link.gen.hostmax,pcie.link.width.current,pcie.link.width.max,index,display_mode,display_active,persistence_mode,addressing_mode,accounting.mode,accounting.buffer_size,driver_model.current,driver_model.pending,vbios_version,inforom.img,inforom.oem,inforom.ecc,inforom.pwr,gpu_recovery_action,gom.current,gom.pending,fan.speed,pstate,clocks_event_reasons.supported,clocks_event_reasons.active,clocks_event_reasons.gpu_idle,clocks_event_reasons.applications_clocks_setting,clocks_event_reasons.sw_power_cap,clocks_event_reasons.hw_slowdown,clocks_event_reasons.hw_thermal_slowdown,clocks_event_reasons.hw_power_brake_slowdown,clocks_event_reasons.sw_thermal_slowdown,clocks_event_reasons.sync_boost,memory.total,memory.reserved,memory.used,memory.free,compute_mode,compute_cap,utilization.gpu,utilization.memory,utilization.encoder,utilization.decoder,utilization.jpeg,utilization.ofa,encoder.stats.sessionCount,encoder.stats.averageFps,encoder.stats.averageLatency,dramEncryption.mode.current,dramEncryption.mode.pending,ecc.mode.current,ecc.mode.pending,ecc.errors.corrected.volatile.device_memory,ecc.errors.corrected.volatile.dram,ecc.errors.corrected.volatile.register_file,ecc.errors.corrected.volatile.l1_cache,ecc.errors.corrected.volatile.l2_cache,ecc.errors.corrected.volatile.texture_memory,ecc.errors.corrected.volatile.cbu,ecc.errors.corrected.volatile.sram,ecc.errors.corrected.volatile.total,ecc.errors.corrected.aggregate.device_memory,ecc.errors.corrected.aggregate.dram,ecc.errors.corrected.aggregate.register_file,ecc.errors.corrected.aggregate.l1_cache,ecc.errors.corrected.aggregate.l2_cache,ecc.errors.corrected.aggregate.texture_memory,ecc.errors.corrected.aggregate.cbu,ecc.errors.corrected.aggregate.sram,ecc.errors.corrected.aggregate.total,ecc.errors.uncorrected.volatile.device_memory,ecc.errors.uncorrected.volatile.dram,ecc.errors.uncorrected.volatile.register_file,ecc.errors.uncorrected.volatile.l1_cache,ecc.errors.uncorrected.volatile.l2_cache,ecc.errors.uncorrected.volatile.texture_memory,ecc.errors.uncorrected.volatile.cbu,ecc.errors.uncorrected.volatile.sram,ecc.errors.uncorrected.volatile.total,ecc.errors.uncorrected.aggregate.device_memory,ecc.errors.uncorrected.aggregate.dram,ecc.errors.uncorrected.aggregate.register_file,ecc.errors.uncorrected.aggregate.l1_cache,ecc.errors.uncorrected.aggregate.l2_cache,ecc.errors.uncorrected.aggregate.texture_memory,ecc.errors.uncorrected.aggregate.cbu,ecc.errors.uncorrected.aggregate.sram,ecc.errors.uncorrected.aggregate.total,ecc.errors.uncorrected.volatile.sram.parity,ecc.errors.uncorrected.volatile.sram.secded,ecc.errors.uncorrected.aggregate.sram.parity,ecc.errors.uncorrected.aggregate.sram.secded,ecc.errors.uncorrected.aggregate.sram.thresholdExceeded,ecc.errors.uncorrected.aggregate.sram.l2,ecc.errors.uncorrected.aggregate.sram.sm,ecc.errors.uncorrected.aggregate.sram.mcu,ecc.errors.uncorrected.aggregate.sram.pcie,ecc.errors.uncorrected.aggregate.sram.other,retired_pages.single_bit_ecc.count,retired_pages.double_bit.count,retired_pages.pending,remapped_rows.correctable,remapped_rows.uncorrectable,remapped_rows.pending,remapped_rows.failure,remapped_rows.histogram.max,remapped_rows.histogram.high,remapped_rows.histogram.partial,remapped_rows.histogram.low,remapped_rows.histogram.none,temperature.gpu,temperature.gpu.tlimit,temperature.memory,power.management,power.draw,power.draw.average,power.draw.instant,power.limit,enforced.power.limit,power.default_limit,power.min_limit,power.max_limit,module.power.draw.average,module.power.draw.instant,module.power.limit,module.enforced.power.limit,module.power.default_limit,module.power.min_limit,module.power.max_limit,clocks.current.graphics,clocks.current.sm,clocks.current.memory,clocks.current.video,clocks.applications.graphics,clocks.applications.memory,clocks.default_applications.graphics,clocks.default_applications.memory,clocks.max.graphics,clocks.max.sm,clocks.max.memory,mig.mode.current,mig.mode.pending,gsp.mode.current,gsp.mode.default,c2c.mode,protected_memory.total,protected_memory.used,protected_memory.free,fabric.state,fabric.status,platform.chassis_serial_number,platform.slot_number,platform.tray_index,platform.host_id,platform.peer_type,platform.module_id,platform.gpu_fabric_guid --format=csv

关键发现:该命令执行时长在 3~10 秒之间波动,明显异常。测试环境中共有 8 块 GPU,其中 2 块正在被算法程序占用,其余 6 块处于空闲状态。

回顾 NVIDIA 官方文档关于 GPU 驱动持久化的说明后,尝试启用持久化模式。


解决方案

临时启用持久化模式

执行以下命令立即启用 GPU 驱动持久化:

1
nvidia-smi -pm 1

再次执行上述查询命令,响应时间降至毫秒级,问题解决

配置开机自启动

为确保系统重启后持久化配置生效,需要配置 systemd 服务:

1. 创建服务配置文件

1
sudo vim /usr/lib/systemd/system/nvidia-persistenced.service

2. 添加以下内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=NVIDIA Persistence Daemon
Wants=network.target

[Service]
Type=forking
PIDFile=/var/run/nvidia-persistenced/nvidia-persistenced.pid
ExecStart=/usr/bin/nvidia-persistenced --persistence-mode
ExecStopPost=/bin/rm -rf /var/run/nvidia-persistenced

[Install]
WantedBy=multi-user.target

3. 启用并启动服务

1
sudo systemctl enable nvidia-persistenced.service --now

验证效果

配置完成后,监控系统恢复正常,GPU 使用情况采集稳定:

Nvidia Exporter Service After Fix


技术原理

GPU 驱动加载机制

NVIDIA GPU 交互依赖内核模式驱动程序,该驱动程序的运行模式分为两种:

  • 持久化模式:驱动程序持续保持活跃状态
  • 按需加载模式:驱动程序仅在有程序使用 GPU 时加载

驱动程序生命周期

初始化阶段
当首个程序尝试与 GPU 交互时,如果内核驱动未运行,系统会触发驱动加载并初始化 GPU 设备。

去初始化阶段
当所有 GPU 客户端程序退出后,驱动程序会执行 GPU 去初始化操作,实质上"关闭" GPU 设备。

对用户的影响

应用启动延迟

首次触发 GPU 初始化时,由于需要执行 ECC 内存检查等操作,会产生 1~3 秒的延迟。若 GPU 已初始化,则无此延迟。

驱动状态丢失

GPU 去初始化后,非持久性状态信息(如功耗限制、时钟频率配置等)会丢失,下次初始化时恢复为默认值。启用持久化模式可避免此问题。


平台差异

Windows 平台

在 Windows 系统中,内核驱动在系统启动时加载,并保持运行直至系统关闭。因此 Windows 用户通常无需关注驱动持久化问题。

注意:驱动重载事件(如 TDR 触发或驱动更新)会导致非持久性状态重置。

Linux 平台

Linux 系统的行为取决于运行环境:

图形界面环境
若 X Server 运行在目标 GPU 上,内核驱动通常会从开机到关机持续活跃,由 X 进程维持连接。

无头服务器环境
在无图形界面的服务器(Headless Server)上,若缺少长期运行的 GPU 客户端,每次应用启动和停止都会触发驱动的加载与卸载。这在 高性能计算(HPC)数据中心环境中极为常见,也是本次故障的根本原因。


最佳实践建议

  1. 生产环境强烈推荐启用 GPU 驱动持久化,特别是无头服务器场景
  2. 使用 systemd 服务确保持久化配置在系统重启后自动生效
  3. 监控系统应在启用持久化后进行充分测试,验证指标采集的稳定性
  4. 定期检查 nvidia-persistenced 服务状态,确保其正常运行

参考资料

面朝大海,春暖花开。
使用 Hugo 构建
主题 StackJimmy 设计