#![recursion_limit = "1024"]
#![allow(non_upper_case_globals)]
extern crate libloading;
extern crate nvml_wrapper_sys as ffi;
pub mod bitmasks;
pub mod device;
pub mod enum_wrappers;
pub mod enums;
pub mod error;
pub mod event;
pub mod gpm;
pub mod high_level;
pub mod nv_link;
pub mod struct_wrappers;
pub mod structs;
#[cfg(test)]
mod test_utils;
pub mod unit;
pub mod vgpu;
pub use crate::device::Device;
pub use crate::event::EventSet;
pub use crate::gpm::GpmSample;
pub use crate::nv_link::NvLink;
pub use crate::unit::Unit;
pub mod sys_exports {
pub mod field_id {
pub use crate::ffi::bindings::field_id::*;
}
}
#[cfg(target_os = "linux")]
use std::convert::TryInto;
#[cfg(target_os = "linux")]
use std::ptr;
use std::{
convert::TryFrom,
ffi::{CStr, CString, OsStr},
mem::{self, ManuallyDrop},
os::raw::{c_int, c_uint},
};
use static_assertions::assert_impl_all;
#[cfg(target_os = "linux")]
use crate::enum_wrappers::device::TopologyLevel;
use crate::error::{nvml_sym, nvml_try, NvmlError};
use crate::ffi::bindings::*;
use crate::struct_wrappers::ExcludedDeviceInfo;
#[cfg(target_os = "linux")]
use crate::struct_wrappers::device::PciInfo;
use crate::struct_wrappers::device::VgpuVersion;
use crate::struct_wrappers::unit::HwbcEntry;
use crate::bitmasks::InitFlags;
#[cfg(not(target_os = "linux"))]
const LIB_PATH: &str = "nvml.dll";
#[cfg(target_os = "linux")]
const LIB_PATH: &str = "libnvidia-ml.so.1";
pub fn cuda_driver_version_major(version: i32) -> i32 {
version / 1000
}
pub fn cuda_driver_version_minor(version: i32) -> i32 {
(version % 1000) / 10
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldIdScheme {
V12,
V13Update1,
}
pub struct Nvml {
lib: ManuallyDrop<NvmlLib>,
field_id_scheme: FieldIdScheme,
}
assert_impl_all!(Nvml: Send, Sync);
impl std::fmt::Debug for Nvml {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("NVML")
}
}
fn detect_field_id_scheme(driver_version: &str) -> FieldIdScheme {
let mut parts = driver_version.split('.');
let major: u32 = parts.next().and_then(|s| s.parse().ok()).unwrap_or(0);
let minor: u32 = parts.next().and_then(|s| s.parse().ok()).unwrap_or(0);
if major > 580 || (major == 580 && minor >= 82) {
FieldIdScheme::V13Update1
} else {
FieldIdScheme::V12
}
}
pub(crate) fn translate_field_id(scheme: FieldIdScheme, id: u32) -> u32 {
if scheme == FieldIdScheme::V12 {
return id;
}
match id {
251..=255 => id + 18,
256..=273 => id - 5,
other => other,
}
}
impl Nvml {
#[doc(alias = "nvmlInit_v2")]
pub fn init() -> Result<Self, NvmlError> {
Self::init_internal(LIB_PATH)
}
fn init_internal(path: impl AsRef<std::ffi::OsStr>) -> Result<Self, NvmlError> {
let lib = unsafe {
let lib = NvmlLib::new(path)?;
let sym = nvml_sym(lib.nvmlInit_v2.as_ref())?;
nvml_try(sym())?;
ManuallyDrop::new(lib)
};
let mut nvml = Self {
lib,
field_id_scheme: FieldIdScheme::V12,
};
nvml.field_id_scheme = nvml
.sys_driver_version()
.map(|v| detect_field_id_scheme(&v))
.unwrap_or(FieldIdScheme::V12);
Ok(nvml)
}
#[doc(alias = "nvmlInitWithFlags")]
pub fn init_with_flags(flags: InitFlags) -> Result<Self, NvmlError> {
Self::init_with_flags_internal(LIB_PATH, flags)
}
fn init_with_flags_internal(
path: impl AsRef<std::ffi::OsStr>,
flags: InitFlags,
) -> Result<Self, NvmlError> {
let lib = unsafe {
let lib = NvmlLib::new(path)?;
let sym = nvml_sym(lib.nvmlInitWithFlags.as_ref())?;
nvml_try(sym(flags.bits()))?;
ManuallyDrop::new(lib)
};
let mut nvml = Self {
lib,
field_id_scheme: FieldIdScheme::V12,
};
nvml.field_id_scheme = nvml
.sys_driver_version()
.map(|v| detect_field_id_scheme(&v))
.unwrap_or(FieldIdScheme::V12);
Ok(nvml)
}
pub fn builder<'a>() -> NvmlBuilder<'a> {
NvmlBuilder::default()
}
pub fn lib(&self) -> &NvmlLib {
&self.lib
}
pub fn field_id_scheme(&self) -> FieldIdScheme {
self.field_id_scheme
}
#[doc(alias = "nvmlShutdown")]
pub fn shutdown(mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.lib.nvmlShutdown.as_ref())?;
unsafe {
nvml_try(sym())?;
}
let lib = unsafe { ManuallyDrop::take(&mut self.lib) };
mem::forget(self);
Ok(lib.__library.close()?)
}
#[doc(alias = "nvmlDeviceGetCount_v2")]
pub fn device_count(&self) -> Result<u32, NvmlError> {
let sym = nvml_sym(self.lib.nvmlDeviceGetCount_v2.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(&mut count))?;
Ok(count)
}
}
#[doc(alias = "nvmlSystemGetDriverVersion")]
pub fn sys_driver_version(&self) -> Result<String, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetDriverVersion.as_ref())?;
unsafe {
let mut version_vec = vec![0; NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE as usize];
nvml_try(sym(
version_vec.as_mut_ptr(),
NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE,
))?;
let version_raw = CStr::from_ptr(version_vec.as_ptr());
Ok(version_raw.to_str()?.into())
}
}
#[doc(alias = "nvmlSystemGetNVMLVersion")]
pub fn sys_nvml_version(&self) -> Result<String, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetNVMLVersion.as_ref())?;
unsafe {
let mut version_vec = vec![0; NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE as usize];
nvml_try(sym(
version_vec.as_mut_ptr(),
NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE,
))?;
let version_raw = CStr::from_ptr(version_vec.as_ptr());
Ok(version_raw.to_str()?.into())
}
}
#[doc(alias = "nvmlSystemGetCudaDriverVersion_v2")]
pub fn sys_cuda_driver_version(&self) -> Result<i32, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetCudaDriverVersion_v2.as_ref())?;
unsafe {
let mut version: c_int = mem::zeroed();
nvml_try(sym(&mut version))?;
Ok(version)
}
}
#[doc(alias = "nvmlSystemGetProcessName")]
pub fn sys_process_name(&self, pid: u32, length: usize) -> Result<String, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetProcessName.as_ref())?;
unsafe {
let mut name_vec = vec![0; length];
nvml_try(sym(pid, name_vec.as_mut_ptr(), length as c_uint))?;
let name_raw = CStr::from_ptr(name_vec.as_ptr());
Ok(name_raw.to_str()?.into())
}
}
#[doc(alias = "nvmlDeviceGetHandleByIndex_v2")]
pub fn device_by_index(&self, index: u32) -> Result<Device<'_>, NvmlError> {
let sym = nvml_sym(self.lib.nvmlDeviceGetHandleByIndex_v2.as_ref())?;
unsafe {
let mut device: nvmlDevice_t = mem::zeroed();
nvml_try(sym(index, &mut device))?;
Ok(Device::new(device, self))
}
}
#[doc(alias = "nvmlDeviceGetHandleByPciBusId_v2")]
pub fn device_by_pci_bus_id<S: AsRef<str>>(
&self,
pci_bus_id: S,
) -> Result<Device<'_>, NvmlError>
where
Vec<u8>: From<S>,
{
let sym = nvml_sym(self.lib.nvmlDeviceGetHandleByPciBusId_v2.as_ref())?;
unsafe {
let c_string = CString::new(pci_bus_id)?;
let mut device: nvmlDevice_t = mem::zeroed();
nvml_try(sym(c_string.as_ptr(), &mut device))?;
Ok(Device::new(device, self))
}
}
#[deprecated(note = "use `.device_by_uuid()`, this errors on dual GPU boards")]
#[doc(alias = "nvmlDeviceGetHandleBySerial")]
pub fn device_by_serial<S: AsRef<str>>(&self, board_serial: S) -> Result<Device<'_>, NvmlError>
where
Vec<u8>: From<S>,
{
let sym = nvml_sym(self.lib.nvmlDeviceGetHandleBySerial.as_ref())?;
unsafe {
let c_string = CString::new(board_serial)?;
let mut device: nvmlDevice_t = mem::zeroed();
nvml_try(sym(c_string.as_ptr(), &mut device))?;
Ok(Device::new(device, self))
}
}
#[doc(alias = "nvmlDeviceGetHandleByUUID")]
pub fn device_by_uuid<S: AsRef<str>>(&self, uuid: S) -> Result<Device<'_>, NvmlError>
where
Vec<u8>: From<S>,
{
let sym = nvml_sym(self.lib.nvmlDeviceGetHandleByUUID.as_ref())?;
unsafe {
let c_string = CString::new(uuid)?;
let mut device: nvmlDevice_t = mem::zeroed();
nvml_try(sym(c_string.as_ptr(), &mut device))?;
Ok(Device::new(device, self))
}
}
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetTopologyCommonAncestor")]
pub fn topology_common_ancestor(
&self,
device1: &Device,
device2: &Device,
) -> Result<TopologyLevel, NvmlError> {
let sym = nvml_sym(self.lib.nvmlDeviceGetTopologyCommonAncestor.as_ref())?;
unsafe {
let mut level: nvmlGpuTopologyLevel_t = mem::zeroed();
nvml_try(sym(device1.handle(), device2.handle(), &mut level))?;
TopologyLevel::try_from(level)
}
}
#[doc(alias = "nvmlUnitGetHandleByIndex")]
pub fn unit_by_index(&self, index: u32) -> Result<Unit<'_>, NvmlError> {
let sym = nvml_sym(self.lib.nvmlUnitGetHandleByIndex.as_ref())?;
unsafe {
let mut unit: nvmlUnit_t = mem::zeroed();
nvml_try(sym(index as c_uint, &mut unit))?;
Ok(Unit::new(unit, self))
}
}
#[doc(alias = "nvmlDeviceOnSameBoard")]
pub fn are_devices_on_same_board(
&self,
device1: &Device,
device2: &Device,
) -> Result<bool, NvmlError> {
let sym = nvml_sym(self.lib.nvmlDeviceOnSameBoard.as_ref())?;
unsafe {
let mut bool_int: c_int = mem::zeroed();
nvml_try(sym(device1.handle(), device2.handle(), &mut bool_int))?;
match bool_int {
0 => Ok(false),
_ => Ok(true),
}
}
}
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlSystemGetTopologyGpuSet")]
pub fn topology_gpu_set(&self, cpu_number: u32) -> Result<Vec<Device<'_>>, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetTopologyGpuSet.as_ref())?;
unsafe {
let mut count = match self.topology_gpu_set_count(cpu_number)? {
0 => return Ok(vec![]),
value => value,
};
let mut devices: Vec<nvmlDevice_t> = vec![mem::zeroed(); count as usize];
nvml_try(sym(cpu_number, &mut count, devices.as_mut_ptr()))?;
Ok(devices.into_iter().map(|d| Device::new(d, self)).collect())
}
}
#[cfg(target_os = "linux")]
fn topology_gpu_set_count(&self, cpu_number: u32) -> Result<c_uint, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetTopologyGpuSet.as_ref())?;
unsafe {
let mut count: c_uint = 0;
nvml_try(sym(cpu_number, &mut count, ptr::null_mut()))?;
Ok(count)
}
}
#[doc(alias = "nvmlSystemGetHicVersion")]
pub fn hic_versions(&self) -> Result<Vec<HwbcEntry>, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetHicVersion.as_ref())?;
unsafe {
let mut count: c_uint = match self.hic_count()? {
0 => return Ok(vec![]),
value => value,
};
let mut hics: Vec<nvmlHwbcEntry_t> = vec![mem::zeroed(); count as usize];
nvml_try(sym(&mut count, hics.as_mut_ptr()))?;
hics.into_iter().map(HwbcEntry::try_from).collect()
}
}
#[doc(alias = "nvmlSystemGetHicVersion")]
pub fn hic_count(&self) -> Result<u32, NvmlError> {
let sym = nvml_sym(self.lib.nvmlSystemGetHicVersion.as_ref())?;
unsafe {
let mut count: c_uint = 1;
let mut hics: [nvmlHwbcEntry_t; 1] = [mem::zeroed()];
match sym(&mut count, hics.as_mut_ptr()) {
nvmlReturn_enum_NVML_SUCCESS | nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => {
Ok(count)
}
other => nvml_try(other).map(|_| 0),
}
}
}
#[doc(alias = "nvmlUnitGetCount")]
pub fn unit_count(&self) -> Result<u32, NvmlError> {
let sym = nvml_sym(self.lib.nvmlUnitGetCount.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(&mut count))?;
Ok(count)
}
}
#[doc(alias = "nvmlEventSetCreate")]
pub fn create_event_set(&self) -> Result<EventSet<'_>, NvmlError> {
let sym = nvml_sym(self.lib.nvmlEventSetCreate.as_ref())?;
unsafe {
let mut set: nvmlEventSet_t = mem::zeroed();
nvml_try(sym(&mut set))?;
Ok(EventSet::new(set, self))
}
}
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceDiscoverGpus")]
pub fn discover_gpus(&self, pci_info: PciInfo) -> Result<(), NvmlError> {
let sym = nvml_sym(self.lib.nvmlDeviceDiscoverGpus.as_ref())?;
unsafe { nvml_try(sym(&mut pci_info.try_into()?)) }
}
#[doc(alias = "nvmlGetExcludedDeviceCount")]
pub fn excluded_device_count(&self) -> Result<u32, NvmlError> {
let sym = nvml_sym(self.lib.nvmlGetExcludedDeviceCount.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(&mut count))?;
Ok(count)
}
}
#[doc(alias = "nvmlGetExcludedDeviceInfoByIndex")]
pub fn excluded_device_info(&self, index: u32) -> Result<ExcludedDeviceInfo, NvmlError> {
let sym = nvml_sym(self.lib.nvmlGetExcludedDeviceInfoByIndex.as_ref())?;
unsafe {
let mut info: nvmlExcludedDeviceInfo_t = mem::zeroed();
nvml_try(sym(index, &mut info))?;
ExcludedDeviceInfo::try_from(info)
}
}
#[doc(alias = "nvmlGetVgpuDriverCapabilities")]
pub fn vgpu_driver_capabilities(
&self,
capability: nvmlVgpuDriverCapability_t,
) -> Result<u32, NvmlError> {
let sym = nvml_sym(self.lib.nvmlGetVgpuDriverCapabilities.as_ref())?;
unsafe {
let mut mask: u32 = mem::zeroed();
nvml_try(sym(capability, &mut mask))?;
Ok(mask)
}
}
#[doc(alias = "nvmlGetVgpuVersion")]
pub fn vgpu_version(&self) -> Result<(VgpuVersion, VgpuVersion), NvmlError> {
let sym = nvml_sym(self.lib.nvmlGetVgpuVersion.as_ref())?;
unsafe {
let mut supported: nvmlVgpuVersion_t = mem::zeroed();
let mut current: nvmlVgpuVersion_t = mem::zeroed();
nvml_try(sym(&mut supported, &mut current))?;
Ok((VgpuVersion::from(supported), VgpuVersion::from(current)))
}
}
#[doc(alias = "nvmlSetVgpuVersion")]
pub fn set_vgpu_version(&self, version: VgpuVersion) -> Result<(), NvmlError> {
let sym = nvml_sym(self.lib.nvmlSetVgpuVersion.as_ref())?;
unsafe { nvml_try(sym(&mut version.as_c())) }
}
}
impl Drop for Nvml {
#[doc(alias = "nvmlShutdown")]
fn drop(&mut self) {
unsafe {
self.lib.nvmlShutdown();
ManuallyDrop::drop(&mut self.lib);
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct NvmlBuilder<'a> {
lib_path: Option<&'a OsStr>,
flags: InitFlags,
}
impl<'a> NvmlBuilder<'a> {
pub fn lib_path(&mut self, path: &'a OsStr) -> &mut Self {
self.lib_path = Some(path);
self
}
pub fn flags(&mut self, flags: InitFlags) -> &mut Self {
self.flags = flags;
self
}
pub fn init(&self) -> Result<Nvml, NvmlError> {
let lib_path = self.lib_path.unwrap_or_else(|| LIB_PATH.as_ref());
if self.flags.is_empty() {
Nvml::init_internal(lib_path)
} else {
Nvml::init_with_flags_internal(lib_path, self.flags)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::bitmasks::InitFlags;
use crate::error::NvmlError;
use crate::test_utils::*;
#[test]
fn init_with_flags() {
Nvml::init_with_flags(InitFlags::NO_GPUS).unwrap();
}
#[test]
fn shutdown() {
test(3, || nvml().shutdown())
}
#[test]
fn device_count() {
test(3, || nvml().device_count())
}
#[test]
fn sys_driver_version() {
test(3, || nvml().sys_driver_version())
}
#[test]
fn sys_nvml_version() {
test(3, || nvml().sys_nvml_version())
}
#[test]
fn sys_cuda_driver_version() {
test(3, || nvml().sys_cuda_driver_version())
}
#[test]
fn sys_cuda_driver_version_major() {
test(3, || {
Ok(cuda_driver_version_major(nvml().sys_cuda_driver_version()?))
})
}
#[test]
fn sys_cuda_driver_version_minor() {
test(3, || {
Ok(cuda_driver_version_minor(nvml().sys_cuda_driver_version()?))
})
}
#[test]
fn sys_process_name() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let processes = device.running_graphics_processes()?;
match nvml.sys_process_name(processes[0].pid, 64) {
Err(NvmlError::NoPermission) => Ok("No permission error".into()),
v => v,
}
})
}
#[test]
fn device_by_index() {
let nvml = nvml();
test(3, || nvml.device_by_index(0))
}
#[test]
fn device_by_pci_bus_id() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let id = device.pci_info()?.bus_id;
nvml.device_by_pci_bus_id(id)
})
}
#[ignore = "my machine does not support this call"]
#[test]
fn device_by_serial() {
let nvml = nvml();
#[allow(deprecated)]
test_with_device(3, &nvml, |device| {
let serial = device.serial()?;
nvml.device_by_serial(serial)
})
}
#[test]
fn device_by_uuid() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let uuid = device.uuid()?;
nvml.device_by_uuid(uuid)
})
}
#[ignore = "my machine does not support this call"]
#[cfg(target_os = "linux")]
#[test]
fn topology_common_ancestor() {
let nvml = nvml();
let device1 = device(&nvml);
let device2 = nvml.device_by_index(1).expect("device");
nvml.topology_common_ancestor(&device1, &device2)
.expect("TopologyLevel");
}
#[test]
#[ignore = "my machine does not support this call"]
fn unit_by_index() {
let nvml = nvml();
test(3, || nvml.unit_by_index(0))
}
#[ignore = "my machine does not support this call"]
#[test]
fn are_devices_on_same_board() {
let nvml = nvml();
let device1 = device(&nvml);
let device2 = nvml.device_by_index(1).expect("device");
nvml.are_devices_on_same_board(&device1, &device2)
.expect("bool");
}
#[cfg(target_os = "linux")]
#[test]
fn topology_gpu_set() {
let nvml = nvml();
test(3, || nvml.topology_gpu_set(0))
}
#[test]
fn hic_version() {
let nvml = nvml();
test(3, || nvml.hic_versions())
}
#[test]
fn unit_count() {
test(3, || nvml().unit_count())
}
#[test]
fn create_event_set() {
let nvml = nvml();
test(3, || nvml.create_event_set())
}
#[cfg(target_os = "linux")]
#[should_panic(expected = "OperatingSystem")]
#[test]
fn discover_gpus() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let pci_info = device.pci_info()?;
match nvml.discover_gpus(pci_info) {
Err(NvmlError::NoPermission) => panic!("NoPermission"),
other => other,
}
})
}
#[test]
fn excluded_device_count() {
let nvml = nvml();
test(3, || nvml.excluded_device_count())
}
#[test]
fn excluded_device_info() {
let nvml = nvml();
if nvml.excluded_device_count().unwrap() > 0 {
test(3, || nvml.excluded_device_info(0))
}
}
#[test]
fn vgpu_driver_capabilities() {
let nvml = nvml();
test(3, || nvml.vgpu_driver_capabilities(1))
}
#[test]
fn vgpu_version() {
let nvml = nvml();
test(3, || nvml.vgpu_version())
}
#[test]
fn set_vgpu_version() {
let nvml = nvml();
test(3, || nvml.set_vgpu_version(VgpuVersion { min: 0, max: 0 }))
}
#[test]
fn detect_field_id_scheme_v12_drivers() {
assert_eq!(detect_field_id_scheme("575.51.03"), FieldIdScheme::V12);
assert_eq!(detect_field_id_scheme("570.86.16"), FieldIdScheme::V12);
assert_eq!(detect_field_id_scheme("580.65.06"), FieldIdScheme::V12);
assert_eq!(detect_field_id_scheme("580.0.0"), FieldIdScheme::V12);
}
#[test]
fn detect_field_id_scheme_v13u1_drivers() {
assert_eq!(
detect_field_id_scheme("580.82.07"),
FieldIdScheme::V13Update1
);
assert_eq!(
detect_field_id_scheme("580.95.05"),
FieldIdScheme::V13Update1
);
assert_eq!(
detect_field_id_scheme("580.126.09"),
FieldIdScheme::V13Update1
);
assert_eq!(detect_field_id_scheme("581.0.0"), FieldIdScheme::V13Update1);
assert_eq!(detect_field_id_scheme("600.0.0"), FieldIdScheme::V13Update1);
}
#[test]
fn detect_field_id_scheme_malformed() {
assert_eq!(detect_field_id_scheme(""), FieldIdScheme::V12);
assert_eq!(detect_field_id_scheme("garbage"), FieldIdScheme::V12);
}
#[test]
fn translate_field_id_v12_is_noop() {
for id in 0..300 {
assert_eq!(translate_field_id(FieldIdScheme::V12, id), id);
}
}
#[test]
fn translate_field_id_v13u1_remaps_affected_range() {
use crate::ffi::bindings::field_id::*;
let v12_clocks_event = [
(NVML_FI_DEV_CLOCKS_EVENT_REASON_SW_THERM_SLOWDOWN, 269),
(NVML_FI_DEV_CLOCKS_EVENT_REASON_HW_THERM_SLOWDOWN, 270),
(NVML_FI_DEV_CLOCKS_EVENT_REASON_HW_POWER_BRAKE_SLOWDOWN, 271),
(NVML_FI_DEV_POWER_SYNC_BALANCING_FREQ, 272),
(NVML_FI_DEV_POWER_SYNC_BALANCING_AF, 273),
];
for (v12_id, expected_v13u1) in v12_clocks_event {
assert_eq!(
translate_field_id(FieldIdScheme::V13Update1, v12_id),
expected_v13u1,
"v12 ID {v12_id} should map to v13U1 ID {expected_v13u1}"
);
}
let v12_pwr_smoothing = [
(NVML_FI_PWR_SMOOTHING_ENABLED, 251),
(NVML_FI_PWR_SMOOTHING_PRIV_LVL, 252),
(NVML_FI_PWR_SMOOTHING_IMM_RAMP_DOWN_ENABLED, 253),
(NVML_FI_PWR_SMOOTHING_APPLIED_TMP_CEIL, 254),
(NVML_FI_PWR_SMOOTHING_APPLIED_TMP_FLOOR, 255),
(NVML_FI_PWR_SMOOTHING_MAX_PERCENT_TMP_FLOOR_SETTING, 256),
(NVML_FI_PWR_SMOOTHING_MIN_PERCENT_TMP_FLOOR_SETTING, 257),
(
NVML_FI_PWR_SMOOTHING_HW_CIRCUITRY_PERCENT_LIFETIME_REMAINING,
258,
),
(NVML_FI_PWR_SMOOTHING_MAX_NUM_PRESET_PROFILES, 259),
(NVML_FI_PWR_SMOOTHING_PROFILE_PERCENT_TMP_FLOOR, 260),
(NVML_FI_PWR_SMOOTHING_PROFILE_RAMP_UP_RATE, 261),
(NVML_FI_PWR_SMOOTHING_PROFILE_RAMP_DOWN_RATE, 262),
(NVML_FI_PWR_SMOOTHING_PROFILE_RAMP_DOWN_HYST_VAL, 263),
(NVML_FI_PWR_SMOOTHING_ACTIVE_PRESET_PROFILE, 264),
(NVML_FI_PWR_SMOOTHING_ADMIN_OVERRIDE_PERCENT_TMP_FLOOR, 265),
(NVML_FI_PWR_SMOOTHING_ADMIN_OVERRIDE_RAMP_UP_RATE, 266),
(NVML_FI_PWR_SMOOTHING_ADMIN_OVERRIDE_RAMP_DOWN_RATE, 267),
(NVML_FI_PWR_SMOOTHING_ADMIN_OVERRIDE_RAMP_DOWN_HYST_VAL, 268),
];
for (v12_id, expected_v13u1) in v12_pwr_smoothing {
assert_eq!(
translate_field_id(FieldIdScheme::V13Update1, v12_id),
expected_v13u1,
"v12 ID {v12_id} should map to v13U1 ID {expected_v13u1}"
);
}
let mut mapped: Vec<u32> = (251..=273)
.map(|id| translate_field_id(FieldIdScheme::V13Update1, id))
.collect();
mapped.sort();
let expected: Vec<u32> = (251..=273).collect();
assert_eq!(
mapped, expected,
"remapping must be a bijection over 251-273"
);
}
#[test]
fn translate_field_id_v13u1_passthrough_outside_range() {
assert_eq!(translate_field_id(FieldIdScheme::V13Update1, 0), 0);
assert_eq!(translate_field_id(FieldIdScheme::V13Update1, 250), 250);
assert_eq!(translate_field_id(FieldIdScheme::V13Update1, 274), 274);
}
}