Skip to content

Commit e6b5840

Browse files
feat: add wrappers for clock offset and pstate functions, min_max_fan_speed (#85)
This PR adds wrappers for the following functions: nvmlDeviceGetClockOffsets nvmlDeviceSetClockOffsets nvmlDeviceGetSupportedPerformanceStates nvmlDeviceGetMinMaxClockOfPState nvmlDeviceGetMinMaxFanSpeed Tests for the new wrappers are also added and unwrapped_functions.txt is updated. All the device enum wrappers also now have Copy derived, as there's no reason not to have it for plain C integer enums.
1 parent c75472d commit e6b5840

File tree

7 files changed

+296
-39
lines changed

7 files changed

+296
-39
lines changed

nvml-wrapper/src/device.rs

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,6 +1840,36 @@ impl<'nvml> Device<'nvml> {
18401840
}
18411841
}
18421842

1843+
/**
1844+
Retrieves the min and max fan speed that user can set for the GPU fan.
1845+
1846+
Returns a (min, max) tuple.
1847+
1848+
# Errors
1849+
1850+
* `Uninitialized`, if the library has not been successfully initialized
1851+
* `InvalidArg`, if this `Device` is invalid
1852+
* `NotSupported`, if this `Device` does not have fans
1853+
* `Unknown`, on any unexpected error
1854+
1855+
# Device Support
1856+
1857+
For all cuda-capable discrete products with fans
1858+
*/
1859+
// Checked against local
1860+
// Tested
1861+
#[doc(alias = "nvmlDeviceGetMinMaxFanSpeed")]
1862+
pub fn min_max_fan_speed(&self) -> Result<(u32, u32), NvmlError> {
1863+
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMinMaxFanSpeed.as_ref())?;
1864+
1865+
unsafe {
1866+
let mut min = mem::zeroed();
1867+
let mut max = mem::zeroed();
1868+
nvml_try(sym(self.device, &mut min, &mut max))?;
1869+
Ok((min, max))
1870+
}
1871+
}
1872+
18431873
/**
18441874
Gets current fan control policy.
18451875
@@ -5210,6 +5240,167 @@ impl<'nvml> Device<'nvml> {
52105240
unsafe { nvml_try(sym(self.device, limit)) }
52115241
}
52125242

5243+
/**
5244+
Retrieve min, max and current clock offset of some clock domain for a given PState
5245+
5246+
# Errors
5247+
5248+
* `Uninitialized`, if the library has not been successfully initialized
5249+
* `InvalidArg`, If device, type or pstate are invalid or both minClockOffsetMHz and maxClockOffsetMHz are NULL
5250+
* `ArgumentVersionMismatch`, if the provided version is invalid/unsupported
5251+
* `NotSupported`, if this `Device` does not support this feature
5252+
5253+
# Device Support
5254+
5255+
Supports Maxwell and newer fully supported devices.
5256+
*/
5257+
// Checked against local
5258+
// Tested
5259+
#[doc(alias = "nvmlDeviceGetClockOffsets")]
5260+
pub fn clock_offset(
5261+
&self,
5262+
clock_type: Clock,
5263+
power_state: PerformanceState,
5264+
) -> Result<ClockOffset, NvmlError> {
5265+
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetClockOffsets.as_ref())?;
5266+
5267+
unsafe {
5268+
// Implements NVML_STRUCT_VERSION(ClockOffset, 1), as detailed in nvml.h
5269+
let version =
5270+
(std::mem::size_of::<nvmlClockOffset_v1_t>() | (1_usize << 24_usize)) as u32;
5271+
5272+
let mut clock_offset = nvmlClockOffset_v1_t {
5273+
version,
5274+
type_: clock_type.as_c(),
5275+
pstate: power_state.as_c(),
5276+
clockOffsetMHz: mem::zeroed(),
5277+
minClockOffsetMHz: mem::zeroed(),
5278+
maxClockOffsetMHz: mem::zeroed(),
5279+
};
5280+
nvml_try(sym(self.device, &mut clock_offset))?;
5281+
ClockOffset::try_from(clock_offset)
5282+
}
5283+
}
5284+
5285+
/**
5286+
Control current clock offset of some clock domain for a given PState
5287+
5288+
# Errors
5289+
5290+
* `Uninitialized`, if the library has not been successfully initialized
5291+
* `NoPermission`, if the user doesn't have permission to perform this operation
5292+
* `InvalidArg`, If device, type or pstate are invalid or both clockOffsetMHz is out of allowed range
5293+
* `ArgumentVersionMismatch`, if the provided version is invalid/unsupported
5294+
5295+
# Device Support
5296+
5297+
Supports Maxwell and newer fully supported devices.
5298+
*/
5299+
// Checked against local
5300+
// Tested (no-run)
5301+
#[doc(alias = "nvmlDeviceSetClockOffsets")]
5302+
pub fn set_clock_offset(
5303+
&mut self,
5304+
clock_type: Clock,
5305+
power_state: PerformanceState,
5306+
offset: i32,
5307+
) -> Result<(), NvmlError> {
5308+
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetClockOffsets.as_ref())?;
5309+
5310+
unsafe {
5311+
// Implements NVML_STRUCT_VERSION(ClockOffset, 1), as detailed in nvml.h
5312+
let version =
5313+
(std::mem::size_of::<nvmlClockOffset_v1_t>() | (1_usize << 24_usize)) as u32;
5314+
5315+
let mut clock_offset = nvmlClockOffset_v1_t {
5316+
version,
5317+
type_: clock_type.as_c(),
5318+
pstate: power_state.as_c(),
5319+
clockOffsetMHz: offset,
5320+
minClockOffsetMHz: 0,
5321+
maxClockOffsetMHz: 0,
5322+
};
5323+
nvml_try(sym(self.device, &mut clock_offset))?;
5324+
Ok(())
5325+
}
5326+
}
5327+
5328+
/**
5329+
Get all supported Performance States (P-States) for the device.
5330+
The number of elements in the returned list will never exceed [`NVML_MAX_GPU_PERF_PSTATES`]`.
5331+
5332+
# Errors
5333+
5334+
* `InsufficientSize`, if the the container supplied was not large enough to hold the resulting list
5335+
* `Uninitialized`, if the library has not been successfully initialized
5336+
* `InvalidArg`, if device or pstates is invalid
5337+
* `NotSupported`, if the device does not support performance state readings
5338+
* `Unknown`, on any unexpected error
5339+
*/
5340+
// Checked against local
5341+
// Tested
5342+
#[doc(alias = "nvmlDeviceGetSupportedPerformanceStates")]
5343+
pub fn supported_performance_states(&self) -> Result<Vec<PerformanceState>, NvmlError> {
5344+
let sym = nvml_sym(
5345+
self.nvml
5346+
.lib
5347+
.nvmlDeviceGetSupportedPerformanceStates
5348+
.as_ref(),
5349+
)?;
5350+
5351+
unsafe {
5352+
let mut pstates =
5353+
[PerformanceState::Unknown.as_c(); NVML_MAX_GPU_PERF_PSTATES as usize];
5354+
// The array size passed to `nvmlDeviceGetSupportedPerformanceStates` must be in bytes, not array length
5355+
let byte_size = mem::size_of_val(&pstates);
5356+
5357+
nvml_try(sym(self.device, pstates.as_mut_ptr(), byte_size as u32))?;
5358+
5359+
pstates
5360+
.into_iter()
5361+
.take_while(|pstate| *pstate != PerformanceState::Unknown.as_c())
5362+
.map(PerformanceState::try_from)
5363+
.collect()
5364+
}
5365+
}
5366+
5367+
/**
5368+
Retrieve min and max clocks of some clock domain for a given PState.
5369+
5370+
Returns a (min, max) tuple.
5371+
5372+
# Errors
5373+
5374+
* `Uninitialized`, if the library has not been successfully initialized
5375+
* `InvalidArg`, if device, type or pstate are invalid or both minClockMHz and maxClockMHz are NULL
5376+
* `NotSupported`, if the device does not support this feature
5377+
*/
5378+
// Checked against local
5379+
// Tested
5380+
#[doc(alias = "nvmlDeviceGetMinMaxClockOfPState")]
5381+
pub fn min_max_clock_of_pstate(
5382+
&self,
5383+
clock_type: Clock,
5384+
pstate: PerformanceState,
5385+
) -> Result<(u32, u32), NvmlError> {
5386+
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMinMaxClockOfPState.as_ref())?;
5387+
5388+
unsafe {
5389+
let mut min: u32 = mem::zeroed();
5390+
let mut max: u32 = mem::zeroed();
5391+
5392+
nvml_try(sym(
5393+
self.device,
5394+
clock_type.as_c(),
5395+
pstate.as_c(),
5396+
&mut min,
5397+
&mut max,
5398+
))?;
5399+
5400+
Ok((min, max))
5401+
}
5402+
}
5403+
52135404
// Event handling methods
52145405

52155406
/**
@@ -6017,6 +6208,12 @@ mod test {
60176208
test_with_device(3, &nvml, |device| device.fan_speed_rpm(0))
60186209
}
60196210

6211+
#[test]
6212+
fn min_max_fan_speed() {
6213+
let nvml = nvml();
6214+
test_with_device(3, &nvml, |device| device.min_max_fan_speed())
6215+
}
6216+
60206217
#[test]
60216218
fn num_fans() {
60226219
let nvml = nvml();
@@ -6198,6 +6395,28 @@ mod test {
61986395
test_with_device(3, &nvml, |device| device.power_management_limit())
61996396
}
62006397

6398+
#[test]
6399+
fn clock_offset() {
6400+
let nvml = nvml();
6401+
test_with_device(3, &nvml, |device| {
6402+
device.clock_offset(Clock::Graphics, PerformanceState::Zero)
6403+
});
6404+
}
6405+
6406+
#[test]
6407+
fn supported_performance_states() {
6408+
let nvml = nvml();
6409+
test_with_device(3, &nvml, |device| device.supported_performance_states());
6410+
}
6411+
6412+
#[test]
6413+
fn min_max_clock_of_pstate() {
6414+
let nvml = nvml();
6415+
test_with_device(3, &nvml, |device| {
6416+
device.min_max_clock_of_pstate(Clock::Graphics, PerformanceState::Zero)
6417+
});
6418+
}
6419+
62016420
#[test]
62026421
fn power_management_limit_constraints() {
62036422
let nvml = nvml();
@@ -6737,6 +6956,17 @@ mod test {
67376956
.expect("set to true")
67386957
}
67396958

6959+
// This modifies device state, so we don't want to actually run the test
6960+
#[allow(dead_code)]
6961+
fn set_clock_offset() {
6962+
let nvml = nvml();
6963+
let mut device = device(&nvml);
6964+
6965+
device
6966+
.set_clock_offset(Clock::Graphics, PerformanceState::Zero, -100)
6967+
.expect("set to true")
6968+
}
6969+
67406970
#[cfg(target_os = "linux")]
67416971
#[allow(unused_variables)]
67426972
#[test]

0 commit comments

Comments
 (0)