nvml-wrapper-0.10.0/Cargo.lock 0000644 00000021410 00000000001 0011515 0 ustar # This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
dependencies = [
"serde",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "darling"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libloading"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"windows-sys",
]
[[package]]
name = "nvml-wrapper"
version = "0.10.0"
dependencies = [
"bitflags",
"libloading",
"nvml-wrapper-sys",
"pretty-bytes",
"serde",
"serde_derive",
"static_assertions",
"thiserror",
"wrapcenum-derive",
]
[[package]]
name = "nvml-wrapper-sys"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "698d45156f28781a4e79652b6ebe2eaa0589057d588d3aec1333f6466f13fcb5"
dependencies = [
"libloading",
]
[[package]]
name = "pretty-bytes"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "009d6edd2c1dbf2e1c0cd48a2f7766e03498d49ada7109a01c6911815c685316"
dependencies = [
"atty",
"getopts",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "wrapcenum-derive"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
nvml-wrapper-0.10.0/Cargo.toml 0000644 00000003045 00000000001 0011544 0 ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.60.0"
name = "nvml-wrapper"
version = "0.10.0"
authors = ["Cldfire"]
description = "A safe and ergonomic Rust wrapper for the NVIDIA Management Library"
documentation = "https://docs.rs/nvml-wrapper"
readme = "README.md"
keywords = [
"nvidia",
"gpu",
"managment",
"monitoring",
"hardware",
]
categories = [
"api-bindings",
"hardware-support",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/Cldfire/nvml-wrapper"
[dependencies.bitflags]
version = "2.4.0"
[dependencies.libloading]
version = "0.8.1"
[dependencies.nvml-wrapper-sys]
version = "0.8.0"
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.serde_derive]
version = "1.0"
optional = true
[dependencies.static_assertions]
version = "1.1"
[dependencies.thiserror]
version = "1.0"
[dependencies.wrapcenum-derive]
version = "0.4.1"
[dev-dependencies.pretty-bytes]
version = "0.2"
[features]
default = []
legacy-functions = ["nvml-wrapper-sys/legacy-functions"]
serde = [
"dep:serde",
"dep:serde_derive",
"bitflags/serde",
]
nvml-wrapper-0.10.0/Cargo.toml.orig 0000644 0000000 0000000 00000001774 00726746425 0015264 0 ustar 0000000 0000000 [package]
name = "nvml-wrapper"
version = "0.10.0"
authors = ["Cldfire"]
description = "A safe and ergonomic Rust wrapper for the NVIDIA Management Library"
readme = "../README.md"
documentation = "https://docs.rs/nvml-wrapper"
repository = "https://github.com/Cldfire/nvml-wrapper"
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.60.0"
keywords = ["nvidia", "gpu", "managment", "monitoring", "hardware"]
categories = ["api-bindings", "hardware-support"]
[features]
default = []
legacy-functions = ["nvml-wrapper-sys/legacy-functions"]
serde = ["dep:serde", "dep:serde_derive", "bitflags/serde"]
[dependencies]
thiserror = "1.0"
bitflags = "2.4.0"
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
nvml-wrapper-sys = { version = "0.8.0", path = "../nvml-wrapper-sys" }
wrapcenum-derive = "0.4.1"
libloading = "0.8.1"
static_assertions = "1.1"
[dev-dependencies]
# Used in the `basic_usage` example
pretty-bytes = "0.2"
nvml-wrapper-0.10.0/README.md 0000644 0000000 0000000 00000010752 00726746425 0013650 0 ustar 0000000 0000000 # nvml-wrapper
[](https://docs.rs/nvml-wrapper)
[](https://crates.io/crates/nvml-wrapper)
[](https://crates.io/crates/nvml-wrapper)

[](https://deps.rs/repo/github/cldfire/nvml-wrapper)
A safe and ergonomic Rust wrapper for the [NVIDIA Management Library][nvml] (NVML),
a C-based programmatic interface for monitoring and managing various states within
NVIDIA GPUs.
```rust
use nvml_wrapper::Nvml;
let nvml = Nvml::init()?;
// Get the first `Device` (GPU) in the system
let device = nvml.device_by_index(0)?;
let brand = device.brand()?; // GeForce on my system
let fan_speed = device.fan_speed(0)?; // Currently 17% on my system
let power_limit = device.enforced_power_limit()?; // 275k milliwatts on my system
let encoder_util = device.encoder_utilization()?; // Currently 0 on my system; Not encoding anything
let memory_info = device.memory_info()?; // Currently 1.63/6.37 GB used on my system
// ... and there's a whole lot more you can do. Most everything in NVML is wrapped and ready to go
```
_try the [`basic_usage`](nvml-wrapper/examples/basic_usage.rs) example on your system_
NVML is intended to be a platform for building 3rd-party applications, and is
also the underlying library for NVIDIA's nvidia-smi tool.
## Usage
`nvml-wrapper` builds on top of generated bindings for NVML that make use of the
[`libloading`][libloading] crate. This means the NVML library gets loaded upon
calling `Nvml::init` and can return an error if NVML isn't present, making it
possible to drop NVIDIA-related features in your code at runtime on systems that
don't have relevant hardware.
Successful execution of `Nvml::init` means:
* The NVML library was present on the system and able to be opened
* The function symbol to initialize NVML was loaded and called successfully
* An attempt has been made to load all other NVML function symbols
Every function you call thereafter will individually return an error if it couldn't
be loaded from the NVML library during the `Nvml::init` call.
Note that it's not advised to repeatedly call `Nvml::init` as the constructor
has to perform all the work of loading the function symbols from the library
each time it gets called. Instead, call `Nvml::init` once and store the resulting
`Nvml` instance somewhere to be accessed throughout the lifetime of your program
(perhaps in a [`once_cell`][once_cell]).
## NVML Support
This wrapper is being developed against and currently supports NVML version
11. Each new version of NVML is guaranteed to be backwards-compatible according
to NVIDIA, so this wrapper should continue to work without issue regardless of
NVML version bumps.
### Legacy Functions
Sometimes there will be function-level API version bumps in new NVML releases.
For example:
```text
nvmlDeviceGetComputeRunningProcesses
nvmlDeviceGetComputeRunningProcesses_v2
nvmlDeviceGetComputeRunningProcesses_v3
```
The older versions of the functions will generally continue to work with the
newer NVML releases; however, the newer function versions will not work with
older NVML installs.
By default this wrapper only provides access to the newest function versions.
Enable the `legacy-functions` feature if you require the ability to call older
functions.
## MSRV
The Minimum Supported Rust Version is currently 1.60.0. I will not go out of my
way to avoid bumping this.
## Cargo Features
The `serde` feature can be toggled on in order to `#[derive(Serialize, Deserialize)]`
for every NVML data structure.
#### License
Licensed under either of Apache License, Version
2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
[nvml]: https://developer.nvidia.com/nvidia-management-library-nvml
[libloading]: https://github.com/nagisa/rust_libloading
[once_cell]: https://docs.rs/once_cell/latest/once_cell/sync/struct.Lazy.html
nvml-wrapper-0.10.0/examples/basic_usage.rs 0000644 0000000 0000000 00000006631 00726746425 0017023 0 ustar 0000000 0000000 use nvml_wrapper::enum_wrappers::device::{Clock, TemperatureSensor};
use nvml_wrapper::error::NvmlError;
use nvml_wrapper::{cuda_driver_version_major, cuda_driver_version_minor, Nvml};
use pretty_bytes::converter::convert;
fn main() -> Result<(), NvmlError> {
let nvml = Nvml::init()?;
let cuda_version = nvml.sys_cuda_driver_version()?;
// Grabbing the first device in the system, whichever one that is.
// If you want to ensure you get the same physical device across reboots,
// get devices via UUID or PCI bus IDs.
let device = nvml.device_by_index(0)?;
// Now we can do whatever we want, like getting some data...
let name = device.name()?;
let temperature = device.temperature(TemperatureSensor::Gpu)?;
let mem_info = device.memory_info()?;
let graphics_clock = device.clock_info(Clock::Graphics)?;
let mem_clock = device.clock_info(Clock::Memory)?;
let link_gen = device.current_pcie_link_gen()?;
let link_speed = device
.pcie_link_speed()
.map(u64::from)
// Convert megabytes to bytes
.map(|x| x * 1000000)?;
let link_width = device.current_pcie_link_width()?;
let max_link_gen = device.max_pcie_link_gen()?;
let max_link_width = device.max_pcie_link_width()?;
let max_link_speed = device
.max_pcie_link_speed()?
.as_integer()
.map(u64::from)
// Convert megabytes to bytes
.map(|x| x * 1000000);
let cuda_cores = device.num_cores()?;
let architecture = device.architecture()?;
// And we can use that data (here we just print it)
print!("\n\n");
println!(
"Your {name} (architecture: {architecture}, CUDA cores: {cuda_cores}) \
is currently sitting at {temperature} °C with a graphics clock of \
{graphics_clock} MHz and a memory clock of {mem_clock} MHz. Memory \
usage is {used_mem} out of an available {total_mem}. Right now the \
device is connected via a PCIe gen {link_gen} x{link_width} interface \
with a transfer rate of {link_speed} per lane; the max your hardware \
supports is PCIe gen {max_link_gen} x{max_link_width} at a transfer \
rate of {max_link_speed} per lane.",
name = name,
temperature = temperature,
graphics_clock = graphics_clock,
mem_clock = mem_clock,
used_mem = convert(mem_info.used as _),
total_mem = convert(mem_info.total as _),
link_gen = link_gen,
// Convert byte output to transfers/sec
link_speed = convert(link_speed as _).replace("B", "T") + "/s",
link_width = link_width,
max_link_gen = max_link_gen,
max_link_width = max_link_width,
cuda_cores = cuda_cores,
architecture = architecture,
max_link_speed = max_link_speed
// Convert byte output to transfers/sec
.map(|x| convert(x as _).replace("B", "T") + "/s")
.unwrap_or_else(|| "".into()),
);
println!();
if device.is_multi_gpu_board()? {
println!("This device is on a multi-GPU board.")
} else {
println!("This device is not on a multi-GPU board.")
}
println!();
println!(
"System CUDA version: {}.{}",
cuda_driver_version_major(cuda_version),
cuda_driver_version_minor(cuda_version)
);
print!("\n\n");
Ok(())
}
nvml-wrapper-0.10.0/examples/event_loop.rs 0000644 0000000 0000000 00000004041 00726746425 0016721 0 ustar 0000000 0000000 #[cfg(target_os = "linux")]
fn main() -> Result<(), nvml_wrapper::error::NvmlErrorWithSource> {
use nvml_wrapper::error::NvmlError;
use nvml_wrapper::Nvml;
// Bringing this in allows us to use `Nvml.create_event_loop()`
use nvml_wrapper::high_level::EventLoopProvider;
// Bringing these in for brevity (Event::SomeEvent vs. SomeEvent)
use nvml_wrapper::high_level::Event::*;
let nvml = Nvml::init()?;
let device = nvml.device_by_index(0)?;
// Create an event loop, registering the single device we obtained above
let mut event_loop = nvml.create_event_loop(vec![&device])?;
// Start handling events
event_loop.run_forever(|event, state| match event {
// If there were no errors, extract the event
Ok(event) => match event {
ClockChange(device) => {
if let Ok(uuid) = device.uuid() {
println!("ClockChange event for device with UUID {:?}", uuid);
} else {
// Your error-handling strategy here
}
}
PowerStateChange(device) => {
if let Ok(uuid) = device.uuid() {
println!("PowerStateChange event for device with UUID {:?}", uuid);
} else {
// Your error-handling strategy here
}
}
_ => println!("A different event occurred: {:?}", event),
},
// If there was an error, handle it
Err(e) => match e {
// If the error is `Unknown`, continue looping and hope for the best
NvmlError::Unknown => {}
// The other errors that can occur are almost guaranteed to mean that
// further looping will never be successful (`GpuLost` and
// `Uninitialized`), so we stop looping
_ => state.interrupt(),
},
});
Ok(())
}
#[cfg(not(target_os = "linux"))]
fn main() {
println!("NVML only supports events on linux :(");
}
nvml-wrapper-0.10.0/src/bitmasks/device.rs 0000644 0000000 0000000 00000010640 00726746425 0016576 0 ustar 0000000 0000000 #![allow(deprecated)]
use crate::ffi::bindings::*;
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
bitflags! {
/// Flags used to specify why a GPU is throttling.
// Checked against local
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ThrottleReasons: u64 {
/// Nothing is running on the GPU.
///
/// This limiter may be removed in a future release.
const GPU_IDLE = nvmlClocksThrottleReasonGpuIdle as u64;
/// GPU clocks are limited by the current applications clocks setting.
const APPLICATIONS_CLOCKS_SETTING = nvmlClocksThrottleReasonApplicationsClocksSetting as u64;
#[deprecated(note = "Renamed to `APPLICATIONS_CLOCKS_SETTING`.")]
const USER_DEFINED_CLOCKS = nvmlClocksThrottleReasonUserDefinedClocks as u64;
/// Software power scaling algorithm is reducing clocks.
const SW_POWER_CAP = nvmlClocksThrottleReasonSwPowerCap as u64;
/**
Hardware slowdown (reducing the core clocks by a factor of 2 or more)
is engaged.
This is an indicator of:
* Temperature being too high
* External Power Brake Asseration being triggered (e.g. by the system power supply)
* Power draw being too high and Fast Trigger protection reducing the clocks
This may also be reported during powerstate or clock change, behavior that may be
removed in a later release.
*/
const HW_SLOWDOWN = nvmlClocksThrottleReasonHwSlowdown as u64;
/**
This GPU is being throttled by another GPU in its sync boost group.
Sync boost groups can be used to maximize performance per watt. All GPUs
in a sync boost group will boost to the minimum possible clocks across
the entire group. Look at the throttle reasons for other GPUs in the
system to find out why this GPU is being held at lower clocks.
*/
const SYNC_BOOST = nvmlClocksThrottleReasonSyncBoost as u64;
/**
Software thermal slowdown.
This is an indicator of one or more of the following:
* The current GPU temperature is above the max GPU operating temperature
* The current memory temperature is above the max memory operating temperature
*/
const SW_THERMAL_SLOWDOWN = nvmlClocksThrottleReasonSwThermalSlowdown as u64;
/**
Hardware thermal slowdown is engaged, reducing core clocks by 2x or more.
This indicates that the temperature of the GPU is too high.
*/
const HW_THERMAL_SLOWDOWN = nvmlClocksThrottleReasonHwThermalSlowdown as u64;
/**
Hardware power brake slowdown is engaged, reducing core clocks by 2x or more.
This indicates that an external power brake assertion is being triggered,
such as by the system power supply.
*/
const HW_POWER_BRAKE_SLOWDOWN = nvmlClocksThrottleReasonHwPowerBrakeSlowdown as u64;
/// GPU clocks are limited by the current setting of display clocks.
const DISPLAY_CLOCK_SETTING = nvmlClocksThrottleReasonDisplayClockSetting as u64;
/// Clocks are as high as possible and are not being throttled.
const NONE = nvmlClocksThrottleReasonNone as u64;
}
}
bitflags! {
/// Flags that specify info about a frame capture session
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct FbcFlags: u32 {
const DIFFMAP_ENABLED = NVML_NVFBC_SESSION_FLAG_DIFFMAP_ENABLED;
const CLASSIFICATIONMAP_ENABLED = NVML_NVFBC_SESSION_FLAG_CLASSIFICATIONMAP_ENABLED;
/// Specifies if capture was requested as a non-blocking call.
const CAPTURE_WITH_WAIT_NO_WAIT = NVML_NVFBC_SESSION_FLAG_CAPTURE_WITH_WAIT_NO_WAIT;
/// Specifies if capture was requested as a blocking call.
const CAPTURE_WITH_WAIT_INFINITE = NVML_NVFBC_SESSION_FLAG_CAPTURE_WITH_WAIT_INFINITE;
/// Specifies if capture was requested as a blocking call with a timeout.
const CAPTURE_WITH_WAIT_TIMEOUT = NVML_NVFBC_SESSION_FLAG_CAPTURE_WITH_WAIT_TIMEOUT;
}
}
nvml-wrapper-0.10.0/src/bitmasks/event.rs 0000644 0000000 0000000 00000003503 00726746425 0016460 0 ustar 0000000 0000000 use crate::ffi::bindings::*;
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
bitflags! {
/**
Event types that you can request to be notified about.
Types can be combined with the Bitwise Or operator `|` when passed to
`Device.register_events()`.
*/
// Checked against local
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct EventTypes: u64 {
/// A corrected texture memory error is not an ECC error, so it does not
/// generate a single bit event.
const SINGLE_BIT_ECC_ERROR = nvmlEventTypeSingleBitEccError as u64;
/// An uncorrected texture memory error is not an ECC error, so it does not
/// generate a double bit event.
const DOUBLE_BIT_ECC_ERROR = nvmlEventTypeDoubleBitEccError as u64;
/**
Power state change event.
On the Fermi architecture, a PState change is an indicator that the GPU
is throttling down due to no work being executed on the GPU, power
capping, or thermal capping. In a typical situation, Fermi-based
GPUs should stay in performance state zero for the duration of the
execution of a compute process.
*/
const PSTATE_CHANGE = nvmlEventTypePState as u64;
const CRITICAL_XID_ERROR = nvmlEventTypeXidCriticalError as u64;
/// Only supports the Kepler architecture.
const CLOCK_CHANGE = nvmlEventTypeClock as u64;
/// Power source change event (battery vs. AC power).
const POWER_SOURCE_CHANGE = nvmlEventTypePowerSourceChange as u64;
/// MIG configuration changes.
const MIG_CONFIG_CHANGE = nvmlEventMigConfigChange as u64;
}
}
nvml-wrapper-0.10.0/src/bitmasks/mod.rs 0000644 0000000 0000000 00000001720 00726746425 0016115 0 ustar 0000000 0000000 pub mod device;
pub mod event;
pub mod nv_link;
use crate::ffi::bindings::*;
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
bitflags! {
/// Generic flags used to specify the default behavior of some functions.
// Checked against local
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Behavior: u32 {
const DEFAULT = nvmlFlagDefault;
const FORCE = nvmlFlagForce;
}
}
bitflags! {
/// Flags that can be passed to `Nvml::init_with_flags()`.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct InitFlags: u32 {
/// Don't fail to initialize when no NVIDIA GPUs are found.
const NO_GPUS = NVML_INIT_FLAG_NO_GPUS;
/// Don't attach GPUs during initialization.
const NO_ATTACH = NVML_INIT_FLAG_NO_ATTACH;
}
}
nvml-wrapper-0.10.0/src/bitmasks/nv_link.rs 0000644 0000000 0000000 00000003103 00726746425 0016773 0 ustar 0000000 0000000 use crate::ffi::bindings::*;
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
bitflags! {
/**
Represents the NvLink utilization counter packet types that can be counted.
Only applicable when `UtilizationCountUnit`s are packets or bytes. All
packet filter descriptions are target GPU centric.
*/
// Checked against local
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct PacketTypes: u32 {
const NO_OP = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_NOP;
const READ = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_READ;
const WRITE = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_WRITE;
/// Reduction atomic requests.
const RATOM = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_RATOM;
/// Non-reduction atomic requests.
const NON_RATOM = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_NRATOM;
/// Flush requests.
const FLUSH = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_FLUSH;
/// Responses with data.
const WITH_DATA = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_RESPDATA;
/// Responses without data.
const NO_DATA = nvmlNvLinkUtilizationCountPktTypes_enum_NVML_NVLINK_COUNTER_PKTFILTER_RESPNODATA;
}
}
nvml-wrapper-0.10.0/src/device.rs 0000644 0000000 0000000 00000626720 00726746425 0014775 0 ustar 0000000 0000000 #[cfg(target_os = "linux")]
use crate::EventSet;
use crate::NvLink;
use crate::Nvml;
use crate::bitmasks::device::ThrottleReasons;
#[cfg(target_os = "linux")]
use crate::bitmasks::event::EventTypes;
#[cfg(target_os = "windows")]
use crate::bitmasks::Behavior;
use crate::enum_wrappers::{bool_from_state, device::*, state_from_bool};
use crate::enums::device::BusType;
use crate::enums::device::DeviceArchitecture;
use crate::enums::device::GpuLockedClocksSetting;
use crate::enums::device::PcieLinkMaxSpeed;
use crate::enums::device::PowerSource;
#[cfg(target_os = "linux")]
use crate::error::NvmlErrorWithSource;
use crate::error::{nvml_sym, nvml_try, Bits, NvmlError};
use crate::ffi::bindings::*;
use crate::struct_wrappers::device::*;
use crate::structs::device::*;
#[cfg(target_os = "linux")]
use std::convert::TryInto;
#[cfg(target_os = "linux")]
use std::os::raw::c_ulong;
use std::{
convert::TryFrom,
ffi::CStr,
mem,
os::raw::{c_int, c_uint, c_ulonglong},
ptr,
};
use static_assertions::assert_impl_all;
/**
Struct that represents a device on the system.
Obtain a `Device` with the various methods available to you on the `Nvml`
struct.
Lifetimes are used to enforce that each `Device` instance cannot be used after
the `Nvml` instance it was obtained from is dropped:
```compile_fail
use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlError> {
let nvml = Nvml::init()?;
let device = nvml.device_by_index(0)?;
drop(nvml);
// This won't compile
device.fan_speed(0)?;
# Ok(())
# }
```
This means you shouldn't have to worry about calls to `Device` methods returning
`Uninitialized` errors.
*/
#[derive(Debug)]
pub struct Device<'nvml> {
device: nvmlDevice_t,
nvml: &'nvml Nvml,
}
unsafe impl<'nvml> Send for Device<'nvml> {}
unsafe impl<'nvml> Sync for Device<'nvml> {}
assert_impl_all!(Device: Send, Sync);
impl<'nvml> Device<'nvml> {
/**
Create a new `Device` wrapper.
You will most likely never need to call this; see the methods available to you
on the `Nvml` struct to get one.
# Safety
It is your responsibility to ensure that the given `nvmlDevice_t` pointer
is valid.
*/
// Clippy bug, see https://github.com/rust-lang/rust-clippy/issues/5593
#[allow(clippy::missing_safety_doc)]
pub unsafe fn new(device: nvmlDevice_t, nvml: &'nvml Nvml) -> Self {
Self { device, nvml }
}
/// Access the `Nvml` reference this struct wraps
pub fn nvml(&self) -> &'nvml Nvml {
self.nvml
}
/// Get the raw device handle contained in this struct
///
/// Sometimes necessary for C interop.
///
/// # Safety
///
/// This is unsafe to prevent it from being used without care.
pub unsafe fn handle(&self) -> nvmlDevice_t {
self.device
}
/**
Clear all affinity bindings for the calling thread.
Note that this was changed as of version 8.0; older versions cleared affinity for
the calling process and all children.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested (no-run)
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceClearCpuAffinity")]
pub fn clear_cpu_affinity(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceClearCpuAffinity.as_ref())?;
unsafe { nvml_try(sym(self.device)) }
}
/**
Gets the root/admin permissions for the target API.
Only root users are able to call functions belonging to restricted APIs. See
the documentation for the `RestrictedApi` enum for a list of those functions.
Non-root users can be granted access to these APIs through use of
`.set_api_restricted()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or the apiType is invalid (may occur if
the C lib changes dramatically?)
* `NotSupported`, if this query is not supported by this `Device` or this `Device`
does not support the feature that is being queried (e.g. enabling/disabling auto
boosted clocks is not supported by this `Device`).
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports all _fully supported_ products.
*/
// Checked against local
// Tested (except for AutoBoostedClocks)
#[doc(alias = "nvmlDeviceGetAPIRestriction")]
pub fn is_api_restricted(&self, api: Api) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetAPIRestriction.as_ref())?;
unsafe {
let mut restricted_state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, api.as_c(), &mut restricted_state))?;
bool_from_state(restricted_state)
}
}
/**
Gets the current clock setting that all applications will use unless an overspec
situation occurs.
This setting can be changed using `.set_applications_clocks()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or the clockType is invalid (may occur
if the C lib changes dramatically?)
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetApplicationsClock")]
pub fn applications_clock(&self, clock_type: Clock) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetApplicationsClock.as_ref())?;
unsafe {
let mut clock: c_uint = mem::zeroed();
nvml_try(sym(self.device, clock_type.as_c(), &mut clock))?;
Ok(clock)
}
}
/**
Gets the current and default state of auto boosted clocks.
Auto boosted clocks are enabled by default on some hardware, allowing the GPU to run
as fast as thermals will allow it to.
On Pascal and newer hardware, auto boosted clocks are controlled through application
clocks. Use `.set_applications_clocks()` and `.reset_applications_clocks()` to control
auto boost behavior.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support auto boosted clocks
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetAutoBoostedClocksEnabled")]
pub fn auto_boosted_clocks_enabled(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetAutoBoostedClocksEnabled.as_ref())?;
unsafe {
let mut is_enabled: nvmlEnableState_t = mem::zeroed();
let mut is_enabled_default: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut is_enabled, &mut is_enabled_default))?;
Ok(AutoBoostClocksEnabledInfo {
is_enabled: bool_from_state(is_enabled)?,
is_enabled_default: bool_from_state(is_enabled_default)?,
})
}
}
/**
Gets the total, available and used size of BAR1 memory.
BAR1 memory is used to map the FB (device memory) so that it can be directly accessed
by the CPU or by 3rd party devices (peer-to-peer on the PCIe bus).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this query
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetBAR1MemoryInfo")]
pub fn bar1_memory_info(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetBAR1MemoryInfo.as_ref())?;
unsafe {
let mut mem_info: nvmlBAR1Memory_t = mem::zeroed();
nvml_try(sym(self.device, &mut mem_info))?;
Ok(mem_info.into())
}
}
/**
Gets the board ID for this `Device`, from 0-N.
Devices with the same boardID indicate GPUs connected to the same PLX. Use in
conjunction with `.is_multi_gpu_board()` to determine if they are on the same
board as well.
The boardID returned is a unique ID for the current config. Uniqueness and
ordering across reboots and system configs is not guaranteed (i.e if a Tesla
K40c returns 0x100 and the two GPUs on a Tesla K10 in the same system return
0x200, it is not guaranteed that they will always return those values. They will,
however, always be different from each other).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetBoardId")]
pub fn board_id(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetBoardId.as_ref())?;
unsafe {
let mut id: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut id))?;
Ok(id)
}
}
/**
Gets the brand of this `Device`.
See the `Brand` enum for documentation of possible values.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, check that error's docs for more info
* `Unknown`, on any unexpected error
*/
// Checked against local nvml.h
// Tested
#[doc(alias = "nvmlDeviceGetBrand")]
pub fn brand(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetBrand.as_ref())?;
unsafe {
let mut brand: nvmlBrandType_t = mem::zeroed();
nvml_try(sym(self.device, &mut brand))?;
Brand::try_from(brand)
}
}
/**
Gets bridge chip information for all bridge chips on the board.
Only applicable to multi-GPU devices.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports all _fully supported_ devices.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetBridgeChipInfo")]
pub fn bridge_chip_info(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetBridgeChipInfo.as_ref())?;
unsafe {
let mut info: nvmlBridgeChipHierarchy_t = mem::zeroed();
nvml_try(sym(self.device, &mut info))?;
BridgeChipHierarchy::try_from(info)
}
}
/**
Gets this `Device`'s current clock speed for the given `Clock` type and `ClockId`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or `clock_type` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested (except for CustomerMaxBoost)
#[doc(alias = "nvmlDeviceGetClock")]
pub fn clock(&self, clock_type: Clock, clock_id: ClockId) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetClock.as_ref())?;
unsafe {
let mut clock: c_uint = mem::zeroed();
nvml_try(sym(
self.device,
clock_type.as_c(),
clock_id.as_c(),
&mut clock,
))?;
Ok(clock)
}
}
/**
Gets this `Device`'s customer-defined maximum boost clock speed for the
given `Clock` type.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or `clock_type` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` or the `clock_type` on this `Device`
does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal and newer fully supported devices.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetMaxCustomerBoostClock")]
pub fn max_customer_boost_clock(&self, clock_type: Clock) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMaxCustomerBoostClock.as_ref())?;
unsafe {
let mut clock: c_uint = mem::zeroed();
nvml_try(sym(self.device, clock_type.as_c(), &mut clock))?;
Ok(clock)
}
}
/**
Gets the current compute mode for this `Device`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, check that error's docs for more info
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetComputeMode")]
pub fn compute_mode(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetComputeMode.as_ref())?;
unsafe {
let mut mode: nvmlComputeMode_t = mem::zeroed();
nvml_try(sym(self.device, &mut mode))?;
ComputeMode::try_from(mode)
}
}
/**
Gets the CUDA compute capability of this `Device`.
The returned version numbers are the same as those returned by
`cuDeviceGetAttribute()` from the CUDA API.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
#[doc(alias = "nvmlDeviceGetCudaComputeCapability")]
pub fn cuda_compute_capability(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetCudaComputeCapability.as_ref())?;
unsafe {
let mut major: c_int = mem::zeroed();
let mut minor: c_int = mem::zeroed();
nvml_try(sym(self.device, &mut major, &mut minor))?;
Ok(CudaComputeCapability { major, minor })
}
}
/**
Gets this `Device`'s current clock speed for the given `Clock` type.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` cannot report the specified clock
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetClockInfo")]
pub fn clock_info(&self, clock_type: Clock) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetClockInfo.as_ref())?;
unsafe {
let mut clock: c_uint = mem::zeroed();
nvml_try(sym(self.device, clock_type.as_c(), &mut clock))?;
Ok(clock)
}
}
/**
Gets information about processes with a compute context running on this `Device`.
This only returns information about running compute processes (such as a CUDA application
with an active context). Graphics applications (OpenGL, DirectX) won't be listed by this
function.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Tested
#[doc(alias = "nvmlDeviceGetComputeRunningProcesses_v3")]
pub fn running_compute_processes(&self) -> Result, NvmlError> {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetComputeRunningProcesses_v3
.as_ref(),
)?;
unsafe {
let mut count: c_uint = match self.running_compute_processes_count()? {
0 => return Ok(vec![]),
value => value,
};
// Add a bit of headroom in case more processes are launched in
// between the above call to get the expected count and the time we
// actually make the call to get data below.
count += 5;
let mut processes: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(self.device, &mut count, processes.as_mut_ptr()))?;
processes.truncate(count as usize);
Ok(processes.into_iter().map(ProcessInfo::from).collect())
}
}
/**
Gets the number of processes with a compute context running on this `Device`.
This only returns the count of running compute processes (such as a CUDA application
with an active context). Graphics applications (OpenGL, DirectX) won't be counted by this
function.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Tested as part of `.running_compute_processes()`
#[doc(alias = "nvmlDeviceGetComputeRunningProcesses_v3")]
pub fn running_compute_processes_count(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetComputeRunningProcesses_v3
.as_ref(),
)?;
unsafe {
// Indicates that we want the count
let mut count: c_uint = 0;
// Passing null doesn't mean we want the count, it's just allowed
match sym(self.device, &mut count, ptr::null_mut()) {
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => Ok(count),
// If success, return 0; otherwise, return error
other => nvml_try(other).map(|_| 0),
}
}
}
/**
Gets information about processes with a compute context running on this `Device`.
This only returns information about running compute processes (such as a CUDA application
with an active context). Graphics applications (OpenGL, DirectX) won't be listed by this
function.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
#[doc(alias = "nvmlDeviceGetComputeRunningProcesses_v2")]
#[cfg(feature = "legacy-functions")]
pub fn running_compute_processes_v2(&self) -> Result, NvmlError> {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetComputeRunningProcesses_v2
.as_ref(),
)?;
unsafe {
let mut count: c_uint = match self.running_compute_processes_count_v2()? {
0 => return Ok(vec![]),
value => value,
};
// Add a bit of headroom in case more processes are launched in
// between the above call to get the expected count and the time we
// actually make the call to get data below.
count += 5;
let mut processes: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(self.device, &mut count, processes.as_mut_ptr()))?;
processes.truncate(count as usize);
Ok(processes.into_iter().map(ProcessInfo::from).collect())
}
}
/**
Gets the number of processes with a compute context running on this `Device`.
This only returns the count of running compute processes (such as a CUDA application
with an active context). Graphics applications (OpenGL, DirectX) won't be counted by this
function.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
#[doc(alias = "nvmlDeviceGetComputeRunningProcesses_v2")]
#[cfg(feature = "legacy-functions")]
pub fn running_compute_processes_count_v2(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetComputeRunningProcesses_v2
.as_ref(),
)?;
unsafe {
// Indicates that we want the count
let mut count: c_uint = 0;
// Passing null doesn't mean we want the count, it's just allowed
match sym(self.device, &mut count, ptr::null_mut()) {
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => Ok(count),
// If success, return 0; otherwise, return error
other => nvml_try(other).map(|_| 0),
}
}
}
/**
Gets a vector of bitmasks with the ideal CPU affinity for this `Device`.
The results are sized to `size`. For example, if processors 0, 1, 32, and 33 are
ideal for this `Device` and `size` == 2, result\[0\] = 0x3, result\[1\] = 0x3.
64 CPUs per unsigned long on 64-bit machines, 32 on 32-bit machines.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `InsufficientSize`, if the passed-in `size` is 0 (must be > 0)
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested
// TODO: Should we trim zeros here or leave it to the caller?
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetCpuAffinity")]
pub fn cpu_affinity(&self, size: usize) -> Result, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetCpuAffinity.as_ref())?;
unsafe {
if size == 0 {
// Return an error containing the minimum size that can be passed.
return Err(NvmlError::InsufficientSize(Some(1)));
}
let mut affinities: Vec = vec![mem::zeroed(); size];
nvml_try(sym(self.device, size as c_uint, affinities.as_mut_ptr()))?;
Ok(affinities)
}
}
/**
Gets the current PCIe link generation.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if PCIe link information is not available
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetCurrPcieLinkGeneration")]
pub fn current_pcie_link_gen(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetCurrPcieLinkGeneration.as_ref())?;
unsafe {
let mut link_gen: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut link_gen))?;
Ok(link_gen)
}
}
/**
Gets the current PCIe link width.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if PCIe link information is not available
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetCurrPcieLinkWidth")]
pub fn current_pcie_link_width(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetCurrPcieLinkWidth.as_ref())?;
unsafe {
let mut link_width: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut link_width))?;
Ok(link_width)
}
}
/**
Gets the current utilization and sampling size (sampling size in μs) for the Decoder.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetDecoderUtilization")]
pub fn decoder_utilization(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetDecoderUtilization.as_ref())?;
unsafe {
let mut utilization: c_uint = mem::zeroed();
let mut sampling_period: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut utilization, &mut sampling_period))?;
Ok(UtilizationInfo {
utilization,
sampling_period,
})
}
}
/**
Gets global statistics for active frame buffer capture sessions on this `Device`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Maxwell or newer fully supported devices.
*/
// tested
#[doc(alias = "nvmlDeviceGetFBCStats")]
pub fn fbc_stats(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetFBCStats.as_ref())?;
unsafe {
let mut fbc_stats: nvmlFBCStats_t = mem::zeroed();
nvml_try(sym(self.device, &mut fbc_stats))?;
Ok(fbc_stats.into())
}
}
/**
Gets information about active frame buffer capture sessions on this `Device`.
Note that information such as the horizontal and vertical resolutions, the
average FPS, and the average latency will be zero if no frames have been
captured since a session was started.
# Errors
* `UnexpectedVariant`, for which you can read the docs for
* `IncorrectBits`, if bits are found in a session's info flags that don't
match the flags in this wrapper
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Maxwell or newer fully supported devices.
*/
// tested
#[doc(alias = "nvmlDeviceGetFBCSessions")]
pub fn fbc_sessions_info(&self) -> Result, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetFBCSessions.as_ref())?;
unsafe {
let mut count: c_uint = match self.fbc_session_count()? {
0 => return Ok(vec![]),
value => value,
};
let mut info: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(self.device, &mut count, info.as_mut_ptr()))?;
info.into_iter().map(FbcSessionInfo::try_from).collect()
}
}
/**
Gets the number of active frame buffer capture sessions on this `Device`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// tested as part of the above
#[doc(alias = "nvmlDeviceGetFBCSessions")]
pub fn fbc_session_count(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetFBCSessions.as_ref())?;
unsafe {
let mut count: c_uint = 0;
nvml_try(sym(self.device, &mut count, ptr::null_mut()))?;
Ok(count)
}
}
/**
Gets the default applications clock that this `Device` boots with or defaults to after
`reset_applications_clocks()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetDefaultApplicationsClock")]
pub fn default_applications_clock(&self, clock_type: Clock) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetDefaultApplicationsClock.as_ref())?;
unsafe {
let mut clock: c_uint = mem::zeroed();
nvml_try(sym(self.device, clock_type.as_c(), &mut clock))?;
Ok(clock)
}
}
/// Not documenting this because it's deprecated. Read NVIDIA's docs if you
/// must use it.
#[deprecated(note = "use `Device.memory_error_counter()`")]
#[doc(alias = "nvmlDeviceGetDetailedEccErrors")]
pub fn detailed_ecc_errors(
&self,
error_type: MemoryError,
counter_type: EccCounter,
) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetDetailedEccErrors.as_ref())?;
unsafe {
let mut counts: nvmlEccErrorCounts_t = mem::zeroed();
nvml_try(sym(
self.device,
error_type.as_c(),
counter_type.as_c(),
&mut counts,
))?;
Ok(counts.into())
}
}
/**
Gets the display active state for this `Device`.
This method indicates whether a display is initialized on this `Device`.
For example, whether or not an X Server is attached to this device and
has allocated memory for the screen.
A display can be active even when no monitor is physically attached to this `Device`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetDisplayActive")]
pub fn is_display_active(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetDisplayActive.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
bool_from_state(state)
}
}
/**
Gets whether a physical display is currently connected to any of this `Device`'s
connectors.
This calls the C function `nvmlDeviceGetDisplayMode`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetDisplayMode")]
pub fn is_display_connected(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetDisplayMode.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
bool_from_state(state)
}
}
/**
Gets the current and pending driver model for this `Device`.
On Windows, the device driver can run in either WDDM or WDM (TCC) modes.
If a display is attached to the device it must run in WDDM mode. TCC mode
is preferred if a display is not attached.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if the platform is not Windows
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
# Platform Support
Only supports Windows.
*/
// Checked against local
// Tested
#[cfg(target_os = "windows")]
#[doc(alias = "nvmlDeviceGetDriverModel")]
pub fn driver_model(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetDriverModel.as_ref())?;
unsafe {
let mut current: nvmlDriverModel_t = mem::zeroed();
let mut pending: nvmlDriverModel_t = mem::zeroed();
nvml_try(sym(self.device, &mut current, &mut pending))?;
Ok(DriverModelState {
current: DriverModel::try_from(current)?,
pending: DriverModel::try_from(pending)?,
})
}
}
/**
Get the current and pending ECC modes for this `Device`.
Changing ECC modes requires a reboot. The "pending" ECC mode refers to the target
mode following the next reboot.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices. Only applicable to devices with
ECC. Requires `InfoRom::ECC` version 1.0 or higher.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetEccMode")]
pub fn is_ecc_enabled(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetEccMode.as_ref())?;
unsafe {
let mut current: nvmlEnableState_t = mem::zeroed();
let mut pending: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut current, &mut pending))?;
Ok(EccModeState {
currently_enabled: bool_from_state(current)?,
pending_enabled: bool_from_state(pending)?,
})
}
}
/**
Gets the current utilization and sampling size (sampling size in μs) for the Encoder.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetEncoderUtilization")]
pub fn encoder_utilization(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetEncoderUtilization.as_ref())?;
unsafe {
let mut utilization: c_uint = mem::zeroed();
let mut sampling_period: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut utilization, &mut sampling_period))?;
Ok(UtilizationInfo {
utilization,
sampling_period,
})
}
}
/**
Gets the current capacity of this device's encoder in macroblocks per second.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this device is invalid
* `NotSupported`, if this `Device` does not support the given `for_type`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Maxwell or newer fully supported devices.
*/
// Tested
#[doc(alias = "nvmlDeviceGetEncoderCapacity")]
pub fn encoder_capacity(&self, for_type: EncoderType) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetEncoderCapacity.as_ref())?;
unsafe {
let mut capacity: c_uint = mem::zeroed();
nvml_try(sym(self.device, for_type.as_c(), &mut capacity))?;
Ok(capacity)
}
}
/**
Gets the current encoder stats for this device.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this device is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Maxwell or newer fully supported devices.
*/
// Tested
#[doc(alias = "nvmlDeviceGetEncoderStats")]
pub fn encoder_stats(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetEncoderStats.as_ref())?;
unsafe {
let mut session_count: c_uint = mem::zeroed();
let mut average_fps: c_uint = mem::zeroed();
let mut average_latency: c_uint = mem::zeroed();
nvml_try(sym(
self.device,
&mut session_count,
&mut average_fps,
&mut average_latency,
))?;
Ok(EncoderStats {
session_count,
average_fps,
average_latency,
})
}
}
/**
Gets information about active encoder sessions on this device.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, if an enum variant not defined in this wrapper gets
returned in a field of an `EncoderSessionInfo` struct
* `Unknown`, on any unexpected error
# Device Support
Supports Maxwell or newer fully supported devices.
*/
// Tested
// TODO: Test this with an active session and make sure it works
#[doc(alias = "nvmlDeviceGetEncoderSessions")]
pub fn encoder_sessions(&self) -> Result, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetEncoderSessions.as_ref())?;
unsafe {
let mut count = match self.encoder_sessions_count()? {
0 => return Ok(vec![]),
value => value,
};
let mut sessions: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(self.device, &mut count, sessions.as_mut_ptr()))?;
sessions.truncate(count as usize);
sessions
.into_iter()
.map(EncoderSessionInfo::try_from)
.collect::>()
}
}
/**
Gets the number of active encoder sessions on this device.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// tested as part of the above
fn encoder_sessions_count(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetEncoderSessions.as_ref())?;
unsafe {
let mut count: c_uint = 0;
nvml_try(sym(self.device, &mut count, ptr::null_mut()))?;
Ok(count)
}
}
/**
Gets the effective power limit in milliwatts that the driver enforces after taking
into account all limiters.
Note: This can be different from the `.power_management_limit()` if other limits
are set elswhere. This includes the out-of-band power limit interface.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetEnforcedPowerLimit")]
pub fn enforced_power_limit(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetEnforcedPowerLimit.as_ref())?;
unsafe {
let mut limit: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut limit))?;
Ok(limit)
}
}
/**
Gets the intended operating speed of the specified fan as a percentage of the
maximum fan speed (100%).
Note: The reported speed is the intended fan speed. If the fan is physically blocked
and unable to spin, the output will not match the actual fan speed.
You can determine valid fan indices using [`Self::num_fans()`].
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or `fan_idx` is invalid
* `NotSupported`, if this `Device` does not have a fan or is newer than Maxwell
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports all discrete products with dedicated fans.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetFanSpeed_v2")]
pub fn fan_speed(&self, fan_idx: u32) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetFanSpeed_v2.as_ref())?;
unsafe {
let mut speed: c_uint = mem::zeroed();
nvml_try(sym(self.device, fan_idx, &mut speed))?;
Ok(speed)
}
}
/**
Gets the number of fans on this [`Device`].
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` does not have a fan
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports all discrete products with dedicated fans.
*/
#[doc(alias = "nvmlDeviceGetNumFans")]
pub fn num_fans(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetNumFans.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut count))?;
Ok(count)
}
}
/**
Gets the current GPU operation mode and the pending one (that it will switch to
after a reboot).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports GK110 M-class and X-class Tesla products from the Kepler family. Modes `LowDP`
and `AllOn` are supported on fully supported GeForce products. Not supported
on Quadro and Tesla C-class products.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetGpuOperationMode")]
pub fn gpu_operation_mode(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetGpuOperationMode.as_ref())?;
unsafe {
let mut current: nvmlGpuOperationMode_t = mem::zeroed();
let mut pending: nvmlGpuOperationMode_t = mem::zeroed();
nvml_try(sym(self.device, &mut current, &mut pending))?;
Ok(OperationModeState {
current: OperationMode::try_from(current)?,
pending: OperationMode::try_from(pending)?,
})
}
}
/**
Gets information about processes with a graphics context running on this `Device`.
This only returns information about graphics based processes (OpenGL, DirectX, etc.).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Tested
#[doc(alias = "nvmlDeviceGetGraphicsRunningProcesses_v3")]
pub fn running_graphics_processes(&self) -> Result, NvmlError> {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetGraphicsRunningProcesses_v3
.as_ref(),
)?;
unsafe {
let mut count: c_uint = match self.running_graphics_processes_count()? {
0 => return Ok(vec![]),
value => value,
};
// Add a bit of headroom in case more processes are launched in
// between the above call to get the expected count and the time we
// actually make the call to get data below.
count += 5;
let mut processes: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(self.device, &mut count, processes.as_mut_ptr()))?;
processes.truncate(count as usize);
Ok(processes.into_iter().map(ProcessInfo::from).collect())
}
}
/**
Gets the number of processes with a graphics context running on this `Device`.
This only returns the count of graphics based processes (OpenGL, DirectX).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
*/
// Tested as part of `.running_graphics_processes()`
#[doc(alias = "nvmlDeviceGetGraphicsRunningProcesses_v3")]
pub fn running_graphics_processes_count(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetGraphicsRunningProcesses_v3
.as_ref(),
)?;
unsafe {
// Indicates that we want the count
let mut count: c_uint = 0;
// Passing null doesn't indicate that we want the count. It's just allowed.
match sym(self.device, &mut count, ptr::null_mut()) {
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => Ok(count),
// If success, return 0; otherwise, return error
other => nvml_try(other).map(|_| 0),
}
}
}
/**
Gets information about processes with a graphics context running on this `Device`.
This only returns information about graphics based processes (OpenGL, DirectX, etc.).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
#[doc(alias = "nvmlDeviceGetGraphicsRunningProcesses_v2")]
#[cfg(feature = "legacy-functions")]
pub fn running_graphics_processes_v2(&self) -> Result, NvmlError> {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetGraphicsRunningProcesses_v2
.as_ref(),
)?;
unsafe {
let mut count: c_uint = match self.running_graphics_processes_count_v2()? {
0 => return Ok(vec![]),
value => value,
};
// Add a bit of headroom in case more processes are launched in
// between the above call to get the expected count and the time we
// actually make the call to get data below.
count += 5;
let mut processes: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(self.device, &mut count, processes.as_mut_ptr()))?;
processes.truncate(count as usize);
Ok(processes.into_iter().map(ProcessInfo::from).collect())
}
}
/**
Gets the number of processes with a graphics context running on this `Device`.
This only returns the count of graphics based processes (OpenGL, DirectX).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
*/
#[doc(alias = "nvmlDeviceGetGraphicsRunningProcesses_v2")]
#[cfg(feature = "legacy-functions")]
pub fn running_graphics_processes_count_v2(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetGraphicsRunningProcesses_v2
.as_ref(),
)?;
unsafe {
// Indicates that we want the count
let mut count: c_uint = 0;
// Passing null doesn't indicate that we want the count. It's just allowed.
match sym(self.device, &mut count, ptr::null_mut()) {
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => Ok(count),
// If success, return 0; otherwise, return error
other => nvml_try(other).map(|_| 0),
}
}
}
/**
Gets utilization stats for relevant currently running processes.
Utilization stats are returned for processes that had a non-zero utilization stat
at some point during the target sample period. Passing `None` as the
`last_seen_timestamp` will target all samples that the driver has buffered; passing
a timestamp retrieved from a previous query will target samples taken since that
timestamp.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Maxwell or newer fully supported devices.
*/
#[doc(alias = "nvmlDeviceGetProcessUtilization")]
pub fn process_utilization_stats(
&self,
last_seen_timestamp: T,
) -> Result, NvmlError>
where
T: Into>,
{
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetProcessUtilization.as_ref())?;
unsafe {
let last_seen_timestamp = last_seen_timestamp.into().unwrap_or(0);
let mut count = match self.process_utilization_stats_count()? {
0 => return Ok(vec![]),
v => v,
};
let mut utilization_samples: Vec =
vec![mem::zeroed(); count as usize];
nvml_try(sym(
self.device,
utilization_samples.as_mut_ptr(),
&mut count,
last_seen_timestamp,
))?;
utilization_samples.truncate(count as usize);
Ok(utilization_samples
.into_iter()
.map(ProcessUtilizationSample::from)
.collect())
}
}
fn process_utilization_stats_count(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetProcessUtilization.as_ref())?;
unsafe {
let mut count: c_uint = 0;
match sym(self.device, ptr::null_mut(), &mut count, 0) {
// Despite being undocumented, this appears to be the correct behavior
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => Ok(count),
other => nvml_try(other).map(|_| 0),
}
}
}
/**
Gets the NVML index of this `Device`.
Keep in mind that the order in which NVML enumerates devices has no guarantees of
consistency between reboots. Also, the NVML index may not correlate with other APIs,
such as the CUDA device index.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetIndex")]
pub fn index(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetIndex.as_ref())?;
unsafe {
let mut index: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut index))?;
Ok(index)
}
}
/**
Gets the checksum of the config stored in this `Device`'s infoROM.
Can be used to make sure that two GPUs have the exact same configuration.
The current checksum takes into account configuration stored in PWR and ECC
infoROM objects. The checksum can change between driver released or when the
user changes the configuration (e.g. disabling/enabling ECC).
# Errors
* `CorruptedInfoROM`, if this `Device`'s checksum couldn't be retrieved due to infoROM corruption
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports all devices with an infoROM.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetInforomConfigurationChecksum")]
pub fn config_checksum(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetInforomConfigurationChecksum
.as_ref(),
)?;
unsafe {
let mut checksum: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut checksum))?;
Ok(checksum)
}
}
/**
Gets the global infoROM image version.
This image version, just like the VBIOS version, uniquely describes the exact version
of the infoROM flashed on the board, in contrast to the infoROM object version which
is only an indicator of supported features.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not have an infoROM
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
* `Unknown`, on any unexpected error
# Device Support
Supports all devices with an infoROM.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetInforomImageVersion")]
pub fn info_rom_image_version(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetInforomImageVersion.as_ref())?;
unsafe {
let mut version_vec = vec![0; NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE as usize];
nvml_try(sym(
self.device,
version_vec.as_mut_ptr(),
NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE,
))?;
let version_raw = CStr::from_ptr(version_vec.as_ptr());
Ok(version_raw.to_str()?.into())
}
}
/**
Gets the version information for this `Device`'s infoROM object, for the passed in
object type.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not have an infoROM
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Utf8Error`, if the string obtained from the C function is not valid UTF-8
* `Unknown`, on any unexpected error
# Device Support
Supports all devices with an infoROM.
Fermi and higher parts have non-volatile on-board memory for persisting device info,
such as aggregate ECC counts. The version of the data structures in this memory may
change from time to time.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetInforomVersion")]
pub fn info_rom_version(&self, object: InfoRom) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetInforomVersion.as_ref())?;
unsafe {
let mut version_vec = vec![0; NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE as usize];
nvml_try(sym(
self.device,
object.as_c(),
version_vec.as_mut_ptr(),
NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE,
))?;
let version_raw = CStr::from_ptr(version_vec.as_ptr());
Ok(version_raw.to_str()?.into())
}
}
/**
Gets the maximum clock speeds for this `Device`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` cannot report the specified `Clock`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
Note: On GPUs from the Fermi family, current P0 (Performance state 0?) clocks
(reported by `.clock_info()`) can differ from max clocks by a few MHz.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetMaxClockInfo")]
pub fn max_clock_info(&self, clock_type: Clock) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMaxClockInfo.as_ref())?;
unsafe {
let mut clock: c_uint = mem::zeroed();
nvml_try(sym(self.device, clock_type.as_c(), &mut clock))?;
Ok(clock)
}
}
/**
Gets the max PCIe link generation possible with this `Device` and system.
For a gen 2 PCIe device attached to a gen 1 PCIe bus, the max link generation
this function will report is generation 1.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if PCIe link information is not available
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetMaxPcieLinkGeneration")]
pub fn max_pcie_link_gen(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMaxPcieLinkGeneration.as_ref())?;
unsafe {
let mut max_gen: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut max_gen))?;
Ok(max_gen)
}
}
/**
Gets the maximum PCIe link width possible with this `Device` and system.
For a device with a 16x PCie bus width attached to an 8x PCIe system bus,
this method will report a max link width of 8.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if PCIe link information is not available
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetMaxPcieLinkWidth")]
pub fn max_pcie_link_width(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMaxPcieLinkWidth.as_ref())?;
unsafe {
let mut max_width: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut max_width))?;
Ok(max_width)
}
}
/**
Gets the requested memory error counter for this `Device`.
Only applicable to devices with ECC. Requires ECC mode to be enabled.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if `error_type`, `counter_type`, or `location` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support ECC error reporting for the specified
memory
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices. Requires `InfoRom::ECC` version
2.0 or higher to report aggregate location-based memory error counts. Requires
`InfoRom::ECC version 1.0 or higher to report all other memory error counts.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetMemoryErrorCounter")]
pub fn memory_error_counter(
&self,
error_type: MemoryError,
counter_type: EccCounter,
location: MemoryLocation,
) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMemoryErrorCounter.as_ref())?;
unsafe {
let mut count: c_ulonglong = mem::zeroed();
nvml_try(sym(
self.device,
error_type.as_c(),
counter_type.as_c(),
location.as_c(),
&mut count,
))?;
Ok(count)
}
}
/**
Gets the amount of used, free and total memory available on this `Device`, in bytes.
Note that enabling ECC reduces the amount of total available memory due to the
extra required parity bits.
Also note that on Windows, most device memory is allocated and managed on startup
by Windows.
Under Linux and Windows TCC (no physical display connected), the reported amount
of used memory is equal to the sum of memory allocated by all active channels on
this `Device`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetMemoryInfo")]
pub fn memory_info(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMemoryInfo.as_ref())?;
unsafe {
let mut info: nvmlMemory_t = mem::zeroed();
nvml_try(sym(self.device, &mut info))?;
Ok(info.into())
}
}
/**
Gets the minor number for this `Device`.
The minor number is such that the NVIDIA device node file for each GPU will
have the form `/dev/nvidia[minor number]`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetMinorNumber")]
pub fn minor_number(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMinorNumber.as_ref())?;
unsafe {
let mut number: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut number))?;
Ok(number)
}
}
/**
Identifies whether or not this `Device` is on a multi-GPU board.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetMultiGpuBoard")]
pub fn is_multi_gpu_board(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMultiGpuBoard.as_ref())?;
unsafe {
let mut int_bool: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut int_bool))?;
match int_bool {
0 => Ok(false),
_ => Ok(true),
}
}
}
/**
The name of this `Device`, e.g. "Tesla C2070".
The name is an alphanumeric string that denotes a particular product.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetName")]
pub fn name(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetName.as_ref())?;
unsafe {
let mut name_vec = vec![0; NVML_DEVICE_NAME_V2_BUFFER_SIZE as usize];
nvml_try(sym(
self.device,
name_vec.as_mut_ptr(),
NVML_DEVICE_NAME_V2_BUFFER_SIZE,
))?;
let name_raw = CStr::from_ptr(name_vec.as_ptr());
Ok(name_raw.to_str()?.into())
}
}
/**
Gets the PCI attributes of this `Device`.
See `PciInfo` for details about the returned attributes.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `GpuLost`, if the GPU has fallen off the bus or is otherwise inaccessible
* `Utf8Error`, if a string obtained from the C function is not valid Utf8
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPciInfo_v3")]
pub fn pci_info(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPciInfo_v3.as_ref())?;
unsafe {
let mut pci_info: nvmlPciInfo_t = mem::zeroed();
nvml_try(sym(self.device, &mut pci_info))?;
PciInfo::try_from(pci_info, true)
}
}
/**
Gets the PCIe replay counter.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPcieReplayCounter")]
pub fn pcie_replay_counter(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPcieReplayCounter.as_ref())?;
unsafe {
let mut value: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut value))?;
Ok(value)
}
}
/**
Gets PCIe utilization information in KB/s.
The function called within this method is querying a byte counter over a 20ms
interval and thus is the PCIE throughput over that interval.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or `counter` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Maxwell and newer fully supported devices.
# Environment Support
This method is not supported on virtual machines running vGPUs.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPcieThroughput")]
pub fn pcie_throughput(&self, counter: PcieUtilCounter) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPcieThroughput.as_ref())?;
unsafe {
let mut throughput: c_uint = mem::zeroed();
nvml_try(sym(self.device, counter.as_c(), &mut throughput))?;
Ok(throughput)
}
}
/**
Gets the current performance state for this `Device`. 0 == max, 15 == min.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPerformanceState")]
pub fn performance_state(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPerformanceState.as_ref())?;
unsafe {
let mut state: nvmlPstates_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
PerformanceState::try_from(state)
}
}
/**
Gets whether or not persistent mode is enabled for this `Device`.
When driver persistence mode is enabled the driver software is not torn down
when the last client disconnects. This feature is disabled by default.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetPersistenceMode")]
pub fn is_in_persistent_mode(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPersistenceMode.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
bool_from_state(state)
}
}
/**
Gets the default power management limit for this `Device`, in milliwatts.
This is the limit that this `Device` boots with.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPowerManagementDefaultLimit")]
pub fn power_management_limit_default(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetPowerManagementDefaultLimit
.as_ref(),
)?;
unsafe {
let mut limit: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut limit))?;
Ok(limit)
}
}
/**
Gets the power management limit associated with this `Device`.
The power limit defines the upper boundary for the card's power draw. If the card's
total power draw reaches this limit, the power management algorithm kicks in.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi or newer fully supported devices.
This reading is only supported if power management mode is supported. See
`.is_power_management_algo_active()`. Yes, it's deprecated, but that's what
NVIDIA's docs said to see.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPowerManagementLimit")]
pub fn power_management_limit(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPowerManagementLimit.as_ref())?;
unsafe {
let mut limit: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut limit))?;
Ok(limit)
}
}
/**
Gets information about possible power management limit values for this `Device`, in milliwatts.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPowerManagementLimitConstraints")]
pub fn power_management_limit_constraints(
&self,
) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetPowerManagementLimitConstraints
.as_ref(),
)?;
unsafe {
let mut min_limit: c_uint = mem::zeroed();
let mut max_limit: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut min_limit, &mut max_limit))?;
Ok(PowerManagementConstraints {
min_limit,
max_limit,
})
}
}
/// Not documenting this because it's deprecated. Read NVIDIA's docs if you
/// must use it.
// Tested
#[deprecated(note = "NVIDIA states that \"this API has been deprecated.\"")]
#[doc(alias = "nvmlDeviceGetPowerManagementMode")]
pub fn is_power_management_algo_active(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPowerManagementMode.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
bool_from_state(state)
}
}
/// Not documenting this because it's deprecated. Read NVIDIA's docs if you
/// must use it.
// Tested
#[deprecated(note = "use `.performance_state()`.")]
#[doc(alias = "nvmlDeviceGetPowerState")]
pub fn power_state(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPowerState.as_ref())?;
unsafe {
let mut state: nvmlPstates_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
PerformanceState::try_from(state)
}
}
/**
Gets the power usage for this GPU and its associated circuitry (memory) in milliwatts.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support power readings
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
This reading is accurate to within +/- 5% of current power draw on Fermi and Kepler GPUs.
It is only supported if power management mode is supported. See `.is_power_management_algo_active()`.
Yes, that is deprecated, but that's what NVIDIA's docs say to see.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetPowerUsage")]
pub fn power_usage(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPowerUsage.as_ref())?;
unsafe {
let mut usage: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut usage))?;
Ok(usage)
}
}
/**
Gets this device's total energy consumption in millijoules (mJ) since the last
driver reload.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support energy readings
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Volta and newer fully supported devices.
*/
#[doc(alias = "nvmlDeviceGetTotalEnergyConsumption")]
pub fn total_energy_consumption(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetTotalEnergyConsumption.as_ref())?;
unsafe {
let mut total: c_ulonglong = mem::zeroed();
nvml_try(sym(self.device, &mut total))?;
Ok(total)
}
}
/**
Gets the list of retired pages filtered by `cause`, including pages pending retirement.
**I cannot verify that this method will work because the call within is not supported
on my dev machine**. Please **verify for yourself** that it works before you use it.
If you are able to test it on your machine, please let me know if it works; if it
doesn't, I would love a PR.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetRetiredPages_v2")]
pub fn retired_pages(&self, cause: RetirementCause) -> Result, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetRetiredPages_v2.as_ref())?;
unsafe {
let mut count = match self.retired_pages_count(&cause)? {
0 => return Ok(vec![]),
value => value,
};
let mut addresses: Vec = vec![mem::zeroed(); count as usize];
let mut timestamps: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(
self.device,
cause.as_c(),
&mut count,
addresses.as_mut_ptr(),
timestamps.as_mut_ptr(),
))?;
Ok(addresses
.into_iter()
.zip(timestamps)
.map(|(address, timestamp)| RetiredPage { address, timestamp })
.collect())
}
}
// Helper for the above function. Returns # of samples that can be queried.
fn retired_pages_count(&self, cause: &RetirementCause) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetRetiredPages.as_ref())?;
unsafe {
let mut count: c_uint = 0;
nvml_try(sym(
self.device,
cause.as_c(),
&mut count,
// All NVIDIA says is that this
// can't be null.
&mut mem::zeroed(),
))?;
Ok(count)
}
}
/**
Gets whether there are pages pending retirement (they need a reboot to fully retire).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetRetiredPagesPendingStatus")]
pub fn are_pages_pending_retired(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetRetiredPagesPendingStatus
.as_ref(),
)?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
bool_from_state(state)
}
}
/**
Gets recent samples for this `Device`.
`last_seen_timestamp` represents the CPU timestamp in μs. Passing in `None`
will fetch all samples maintained in the underlying buffer; you can
alternatively pass in a timestamp retrieved from the date of the previous
query in order to obtain more recent samples.
The advantage of using this method for samples in contrast to polling via
existing methods is to get higher frequency data at a lower polling cost.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `NotFound`, if sample entries are not found
* `UnexpectedVariant`, check that error's docs for more info
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
# Examples
```
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlError> {
# match test() {
# Err(NvmlError::NotFound) => Ok(()),
# other => other,
# }
# }
# fn test() -> Result<(), NvmlError> {
# let nvml = Nvml::init()?;
# let device = nvml.device_by_index(0)?;
use nvml_wrapper::enum_wrappers::device::Sampling;
// Passing `None` indicates that we want all `Power` samples in the sample buffer
let power_samples = device.samples(Sampling::Power, None)?;
// Take the first sample from the vector, if it exists...
if let Some(sample) = power_samples.get(0) {
// ...and now we can get all `ProcessorClock` samples that exist with a later
// timestamp than the `Power` sample.
let newer_clock_samples = device.samples(Sampling::ProcessorClock, sample.timestamp)?;
}
# Ok(())
# }
```
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetSamples")]
pub fn samples(
&self,
sample_type: Sampling,
last_seen_timestamp: T,
) -> Result, NvmlError>
where
T: Into>,
{
let timestamp = last_seen_timestamp.into().unwrap_or(0);
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetSamples.as_ref())?;
unsafe {
let mut val_type: nvmlValueType_t = mem::zeroed();
let mut count = match self.samples_count(&sample_type, timestamp)? {
0 => return Ok(vec![]),
value => value,
};
let mut samples: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(
self.device,
sample_type.as_c(),
timestamp,
&mut val_type,
&mut count,
samples.as_mut_ptr(),
))?;
let val_type_rust = SampleValueType::try_from(val_type)?;
Ok(samples
.into_iter()
.map(|s| Sample::from_tag_and_struct(&val_type_rust, s))
.collect())
}
}
// Helper for the above function. Returns # of samples that can be queried.
fn samples_count(&self, sample_type: &Sampling, timestamp: u64) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetSamples.as_ref())?;
unsafe {
let mut val_type: nvmlValueType_t = mem::zeroed();
let mut count: c_uint = mem::zeroed();
nvml_try(sym(
self.device,
sample_type.as_c(),
timestamp,
&mut val_type,
&mut count,
// Indicates that we want the count
ptr::null_mut(),
))?;
Ok(count)
}
}
/**
Get values for the given slice of `FieldId`s.
NVIDIA's docs say that if any of the `FieldId`s are populated by the same driver
call, the samples for those IDs will be populated by a single call instead of
a call per ID. It would appear, then, that this is essentially a "batch-request"
API path for better performance.
There are too many field ID constants defined in the header to reasonably
wrap them with an enum in this crate. Instead, I've re-exported the defined
ID constants at `nvml_wrapper::sys_exports::field_id::*`; stick those
constants in `FieldId`s for use with this function.
# Errors
## Outer `Result`
* `InvalidArg`, if `id_slice` has a length of zero
## Inner `Result`
* `UnexpectedVariant`, check that error's docs for more info
# Device Support
Device support varies per `FieldId` that you pass in.
*/
// TODO: Example
#[doc(alias = "nvmlDeviceGetFieldValues")]
pub fn field_values_for(
&self,
id_slice: &[FieldId],
) -> Result>, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetFieldValues.as_ref())?;
unsafe {
let values_count = id_slice.len();
let mut field_values: Vec = Vec::with_capacity(values_count);
for id in id_slice.iter() {
let mut raw: nvmlFieldValue_t = mem::zeroed();
raw.fieldId = id.0;
field_values.push(raw);
}
nvml_try(sym(
self.device,
values_count as i32,
field_values.as_mut_ptr(),
))?;
Ok(field_values
.into_iter()
.map(FieldValueSample::try_from)
.collect())
}
}
/**
Gets the globally unique board serial number associated with this `Device`'s board
as an alphanumeric string.
This serial number matches the serial number tag that is physically attached to the board.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
* `Unknown`, on any unexpected error
# Device Support
Supports all products with an infoROM.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetSerial")]
pub fn serial(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetSerial.as_ref())?;
unsafe {
let mut serial_vec = vec![0; NVML_DEVICE_SERIAL_BUFFER_SIZE as usize];
nvml_try(sym(
self.device,
serial_vec.as_mut_ptr(),
NVML_DEVICE_SERIAL_BUFFER_SIZE,
))?;
let serial_raw = CStr::from_ptr(serial_vec.as_ptr());
Ok(serial_raw.to_str()?.into())
}
}
/**
Gets the board part number for this `Device`.
The board part number is programmed into the board's infoROM.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if the necessary VBIOS fields have not been filled
* `GpuLost`, if the target GPU has fellen off the bus or is otherwise inaccessible
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetBoardPartNumber")]
pub fn board_part_number(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetBoardPartNumber.as_ref())?;
unsafe {
let mut part_num_vec = vec![0; NVML_DEVICE_PART_NUMBER_BUFFER_SIZE as usize];
nvml_try(sym(
self.device,
part_num_vec.as_mut_ptr(),
NVML_DEVICE_PART_NUMBER_BUFFER_SIZE,
))?;
let part_num_raw = CStr::from_ptr(part_num_vec.as_ptr());
Ok(part_num_raw.to_str()?.into())
}
}
/**
Gets current throttling reasons.
Note that multiple reasons can be affecting clocks at once.
The returned bitmask is created via the `ThrottleReasons::from_bits_truncate`
method, meaning that any bits that don't correspond to flags present in this
version of the wrapper will be dropped.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports all _fully supported_ devices.
*/
// Checked against local.
// Tested
#[doc(alias = "nvmlDeviceGetCurrentClocksThrottleReasons")]
pub fn current_throttle_reasons(&self) -> Result {
Ok(ThrottleReasons::from_bits_truncate(
self.current_throttle_reasons_raw()?,
))
}
/**
Gets current throttling reasons, erroring if any bits correspond to
non-present flags.
Note that multiple reasons can be affecting clocks at once.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `IncorrectBits`, if NVML returns any bits that do not correspond to flags in
`ThrottleReasons`
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports all _fully supported_ devices.
*/
// Checked against local.
// Tested
pub fn current_throttle_reasons_strict(&self) -> Result {
let reasons = self.current_throttle_reasons_raw()?;
ThrottleReasons::from_bits(reasons).ok_or(NvmlError::IncorrectBits(Bits::U64(reasons)))
}
// Helper for the above methods.
fn current_throttle_reasons_raw(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetCurrentClocksThrottleReasons
.as_ref(),
)?;
unsafe {
let mut reasons: c_ulonglong = mem::zeroed();
nvml_try(sym(self.device, &mut reasons))?;
Ok(reasons)
}
}
/**
Gets a bitmask of the supported throttle reasons.
These reasons can be returned by `.current_throttle_reasons()`.
The returned bitmask is created via the `ThrottleReasons::from_bits_truncate`
method, meaning that any bits that don't correspond to flags present in this
version of the wrapper will be dropped.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports all _fully supported_ devices.
# Environment Support
This method is not supported on virtual machines running vGPUs.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetSupportedClocksThrottleReasons")]
pub fn supported_throttle_reasons(&self) -> Result {
Ok(ThrottleReasons::from_bits_truncate(
self.supported_throttle_reasons_raw()?,
))
}
/**
Gets a bitmask of the supported throttle reasons, erroring if any bits
correspond to non-present flags.
These reasons can be returned by `.current_throttle_reasons()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `IncorrectBits`, if NVML returns any bits that do not correspond to flags in
`ThrottleReasons`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports all _fully supported_ devices.
# Environment Support
This method is not supported on virtual machines running vGPUs.
*/
// Checked against local
// Tested
pub fn supported_throttle_reasons_strict(&self) -> Result {
let reasons = self.supported_throttle_reasons_raw()?;
ThrottleReasons::from_bits(reasons).ok_or(NvmlError::IncorrectBits(Bits::U64(reasons)))
}
// Helper for the above methods.
fn supported_throttle_reasons_raw(&self) -> Result {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceGetSupportedClocksThrottleReasons
.as_ref(),
)?;
unsafe {
let mut reasons: c_ulonglong = mem::zeroed();
nvml_try(sym(self.device, &mut reasons))?;
Ok(reasons)
}
}
/**
Gets a `Vec` of possible graphics clocks that can be used as an arg for
`set_applications_clocks()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotFound`, if the specified `for_mem_clock` is not a supported frequency
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetSupportedGraphicsClocks")]
pub fn supported_graphics_clocks(&self, for_mem_clock: u32) -> Result, NvmlError> {
match self.supported_graphics_clocks_manual(for_mem_clock, 128) {
Err(NvmlError::InsufficientSize(Some(s))) =>
// `s` is the required size for the call; make the call a second time
{
self.supported_graphics_clocks_manual(for_mem_clock, s)
}
value => value,
}
}
// Removes code duplication in the above function.
fn supported_graphics_clocks_manual(
&self,
for_mem_clock: u32,
size: usize,
) -> Result, NvmlError> {
let mut items: Vec = vec![0; size];
let mut count = size as c_uint;
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetSupportedGraphicsClocks.as_ref())?;
unsafe {
match sym(self.device, for_mem_clock, &mut count, items.as_mut_ptr()) {
// `count` is now the size that is required. Return it in the error.
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => {
return Err(NvmlError::InsufficientSize(Some(count as usize)))
}
value => nvml_try(value)?,
}
}
items.truncate(count as usize);
Ok(items)
}
/**
Gets a `Vec` of possible memory clocks that can be used as an arg for
`set_applications_clocks()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetSupportedMemoryClocks")]
pub fn supported_memory_clocks(&self) -> Result, NvmlError> {
match self.supported_memory_clocks_manual(16) {
Err(NvmlError::InsufficientSize(Some(s))) => {
// `s` is the required size for the call; make the call a second time
self.supported_memory_clocks_manual(s)
}
value => value,
}
}
// Removes code duplication in the above function.
fn supported_memory_clocks_manual(&self, size: usize) -> Result, NvmlError> {
let mut items: Vec = vec![0; size];
let mut count = size as c_uint;
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetSupportedMemoryClocks.as_ref())?;
unsafe {
match sym(self.device, &mut count, items.as_mut_ptr()) {
// `count` is now the size that is required. Return it in the error.
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => {
return Err(NvmlError::InsufficientSize(Some(count as usize)))
}
value => nvml_try(value)?,
}
}
items.truncate(count as usize);
Ok(items)
}
/**
Gets the current temperature readings for the given sensor, in °C.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or `sensor` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not have the specified sensor
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetTemperature")]
pub fn temperature(&self, sensor: TemperatureSensor) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetTemperature.as_ref())?;
unsafe {
let mut temp: c_uint = mem::zeroed();
nvml_try(sym(self.device, sensor.as_c(), &mut temp))?;
Ok(temp)
}
}
/**
Gets the temperature threshold for this `Device` and the specified `threshold_type`, in °C.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or `threshold_type` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not have a temperature sensor or is unsupported
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetTemperatureThreshold")]
pub fn temperature_threshold(
&self,
threshold_type: TemperatureThreshold,
) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetTemperatureThreshold.as_ref())?;
unsafe {
let mut temp: c_uint = mem::zeroed();
nvml_try(sym(self.device, threshold_type.as_c(), &mut temp))?;
Ok(temp)
}
}
/**
Gets the common ancestor for two devices.
# Errors
* `InvalidArg`, if either `Device` is invalid
* `NotSupported`, if this `Device` or the OS does not support this feature
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, an error has occurred in the underlying topology discovery
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetTopologyCommonAncestor")]
pub fn topology_common_ancestor(
&self,
other_device: Device,
) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetTopologyCommonAncestor.as_ref())?;
unsafe {
let mut level: nvmlGpuTopologyLevel_t = mem::zeroed();
nvml_try(sym(self.device, other_device.device, &mut level))?;
TopologyLevel::try_from(level)
}
}
/**
Gets the set of GPUs that are nearest to this `Device` at a specific interconnectivity level.
# Errors
* `InvalidArg`, if this `Device` is invalid or `level` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` or the OS does not support this feature
* `Unknown`, an error has occurred in the underlying topology discovery
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetTopologyNearestGpus")]
pub fn topology_nearest_gpus(
&self,
level: TopologyLevel,
) -> Result>, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetTopologyNearestGpus.as_ref())?;
unsafe {
let mut count = match self.top_nearest_gpus_count(&level)? {
0 => return Ok(vec![]),
value => value,
};
let mut gpus: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(
self.device,
level.as_c(),
&mut count,
gpus.as_mut_ptr(),
))?;
Ok(gpus
.into_iter()
.map(|d| Device::new(d, self.nvml))
.collect())
}
}
// Helper for the above function. Returns # of GPUs in the set.
#[cfg(target_os = "linux")]
fn top_nearest_gpus_count(&self, level: &TopologyLevel) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetTopologyNearestGpus.as_ref())?;
unsafe {
let mut count: c_uint = 0;
nvml_try(sym(
self.device,
level.as_c(),
&mut count,
// Passing null (I assume?)
// indicates that we want the
// GPU count
ptr::null_mut(),
))?;
Ok(count)
}
}
/**
Gets the total ECC error counts for this `Device`.
Only applicable to devices with ECC. The total error count is the sum of errors across
each of the separate memory systems, i.e. the total set of errors across the entire device.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or either enum is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices. Requires `InfoRom::ECC` version 1.0
or higher. Requires ECC mode to be enabled.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceGetTotalEccErrors")]
pub fn total_ecc_errors(
&self,
error_type: MemoryError,
counter_type: EccCounter,
) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetTotalEccErrors.as_ref())?;
unsafe {
let mut count: c_ulonglong = mem::zeroed();
nvml_try(sym(
self.device,
error_type.as_c(),
counter_type.as_c(),
&mut count,
))?;
Ok(count)
}
}
/**
Gets the globally unique immutable UUID associated with this `Device` as a 5 part
hexadecimal string.
This UUID augments the immutable, board serial identifier. It is a globally unique
identifier and is the _only_ available identifier for pre-Fermi-architecture products.
It does NOT correspond to any identifier printed on the board.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
* `Unknown`, on any unexpected error
# Examples
The UUID can be used to compare two `Device`s and find out if they represent
the same physical device:
```no_run
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlError> {
# let nvml = Nvml::init()?;
# let device1 = nvml.device_by_index(0)?;
# let device2 = nvml.device_by_index(1)?;
if device1.uuid()? == device2.uuid()? {
println!("`device1` represents the same physical device that `device2` does.");
}
# Ok(())
# }
```
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetUUID")]
pub fn uuid(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetUUID.as_ref())?;
unsafe {
let mut uuid_vec = vec![0; NVML_DEVICE_UUID_V2_BUFFER_SIZE as usize];
nvml_try(sym(
self.device,
uuid_vec.as_mut_ptr(),
NVML_DEVICE_UUID_V2_BUFFER_SIZE,
))?;
let uuid_raw = CStr::from_ptr(uuid_vec.as_ptr());
Ok(uuid_raw.to_str()?.into())
}
}
/**
Gets the current utilization rates for this `Device`'s major subsystems.
Note: During driver initialization when ECC is enabled, one can see high GPU
and memory utilization readings. This is caused by the ECC memory scrubbing
mechanism that is performed during driver initialization.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetUtilizationRates")]
pub fn utilization_rates(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetUtilizationRates.as_ref())?;
unsafe {
let mut utilization: nvmlUtilization_t = mem::zeroed();
nvml_try(sym(self.device, &mut utilization))?;
Ok(utilization.into())
}
}
/**
Gets the VBIOS version of this `Device`.
The VBIOS version may change from time to time.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Utf8Error`, if the string obtained from the C function is not valid UTF-8
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetVbiosVersion")]
pub fn vbios_version(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetVbiosVersion.as_ref())?;
unsafe {
let mut version_vec = vec![0; NVML_DEVICE_VBIOS_VERSION_BUFFER_SIZE as usize];
nvml_try(sym(
self.device,
version_vec.as_mut_ptr(),
NVML_DEVICE_VBIOS_VERSION_BUFFER_SIZE,
))?;
let version_raw = CStr::from_ptr(version_vec.as_ptr());
Ok(version_raw.to_str()?.into())
}
}
/**
Gets the duration of time during which this `Device` was throttled (lower than the
requested clocks) due to power or thermal constraints.
This is important to users who are trying to understand if their GPUs throttle at any
point while running applications. The difference in violation times at two different
reference times gives the indication of a GPU throttling event.
Violation for thermal capping is not supported at this time.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if this `Device` is invalid or `perf_policy` is invalid (shouldn't occur?)
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
# Device Support
Supports Kepler or newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetViolationStatus")]
pub fn violation_status(
&self,
perf_policy: PerformancePolicy,
) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetViolationStatus.as_ref())?;
unsafe {
let mut viol_time: nvmlViolationTime_t = mem::zeroed();
nvml_try(sym(self.device, perf_policy.as_c(), &mut viol_time))?;
Ok(viol_time.into())
}
}
/**
Gets the interrupt number for this [`Device`].
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
*/
#[doc(alias = "nvmlDeviceGetIrqNum")]
pub fn irq_num(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetIrqNum.as_ref())?;
let irq_num = unsafe {
let mut irq_num: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut irq_num))?;
irq_num
};
Ok(irq_num)
}
/**
Gets the core count for this [`Device`].
The cores represented in the count here are commonly referred to as
"CUDA cores".
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
*/
#[doc(alias = "nvmlDeviceGetNumGpuCores")]
pub fn num_cores(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetNumGpuCores.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut count))?;
Ok(count)
}
}
/**
Gets the power source of this [`Device`].
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
*/
#[doc(alias = "nvmlDeviceGetPowerSource")]
pub fn power_source(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPowerSource.as_ref())?;
let power_source_c = unsafe {
let mut power_source: nvmlPowerSource_t = mem::zeroed();
nvml_try(sym(self.device, &mut power_source))?;
power_source
};
PowerSource::try_from(power_source_c)
}
/**
Gets the memory bus width of this [`Device`].
The returned value is in bits (i.e. 320 for a 320-bit bus width).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
*/
#[doc(alias = "nvmlDeviceGetMemoryBusWidth")]
pub fn memory_bus_width(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetMemoryBusWidth.as_ref())?;
let memory_bus_width = unsafe {
let mut memory_bus_width: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut memory_bus_width))?;
memory_bus_width
};
Ok(memory_bus_width)
}
/**
Gets the max PCIe link speed for this [`Device`].
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
*/
#[doc(alias = "nvmlDeviceGetPcieLinkMaxSpeed")]
pub fn max_pcie_link_speed(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPcieLinkMaxSpeed.as_ref())?;
let pcie_link_max_speed_c = unsafe {
let mut pcie_link_max_speed: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut pcie_link_max_speed))?;
pcie_link_max_speed
};
PcieLinkMaxSpeed::try_from(pcie_link_max_speed_c)
}
/**
Gets the current PCIe link speed for this [`Device`].
NVML docs say the returned value is in "MBPS". Looking at the output of
this function, however, seems to imply it actually returns the transfer
rate per lane of the PCIe link in MT/s, not the combined multi-lane
throughput. See [`PcieLinkMaxSpeed`] for the same discussion.
For example, on my machine currently:
> Right now the device is connected via a PCIe gen 4 x16 interface and
> `pcie_link_speed()` returns 16000
This lines up with the "transfer rate per lane numbers" listed at
. PCIe gen 4 provides 16.0 GT/s.
Also, checking my machine at a different moment yields:
> Right now the device is connected via a PCIe gen 2 x16 interface and
> `pcie_link_speed()` returns 5000
Which again lines up with the table on the page above; PCIe gen 2 provides
5.0 GT/s.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this query is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
*/
#[doc(alias = "nvmlDeviceGetPcieSpeed")]
pub fn pcie_link_speed(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetPcieSpeed.as_ref())?;
let pcie_speed_c = unsafe {
let mut pcie_speed: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut pcie_speed))?;
pcie_speed
};
Ok(pcie_speed_c)
}
/**
Gets the type of bus by which this [`Device`] is connected.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
*/
#[doc(alias = "nvmlDeviceGetBusType")]
pub fn bus_type(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetBusType.as_ref())?;
let bus_type_c = unsafe {
let mut bus_type: nvmlBusType_t = mem::zeroed();
nvml_try(sym(self.device, &mut bus_type))?;
bus_type
};
BusType::try_from(bus_type_c)
}
/**
Gets the architecture of this [`Device`].
# Errors
* `Uninitialized`, if the library has not been successfully initialized
*/
#[doc(alias = "nvmlDeviceGetArchitecture")]
pub fn architecture(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetArchitecture.as_ref())?;
let architecture_c = unsafe {
let mut architecture: nvmlDeviceArchitecture_t = mem::zeroed();
nvml_try(sym(self.device, &mut architecture))?;
architecture
};
DeviceArchitecture::try_from(architecture_c)
}
/**
Checks if this `Device` and the passed-in device are on the same physical board.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if either `Device` is invalid
* `NotSupported`, if this check is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceOnSameBoard")]
pub fn is_on_same_board_as(&self, other_device: &Device) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceOnSameBoard.as_ref())?;
unsafe {
let mut bool_int: c_int = mem::zeroed();
nvml_try(sym(self.device, other_device.handle(), &mut bool_int))?;
#[allow(clippy::match_like_matches_macro)]
Ok(match bool_int {
0 => false,
_ => true,
})
}
}
/**
Resets the application clock to the default value.
This is the applications clock that will be used after a system reboot or a driver
reload. The default value is a constant, but the current value be changed with
`.set_applications_clocks()`.
On Pascal and newer hardware, if clocks were previously locked with
`.set_applications_clocks()`, this call will unlock clocks. This returns clocks
to their default behavior of automatically boosting above base clocks as
thermal limits allow.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer non-GeForce fully supported devices and Maxwell or newer
GeForce devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceResetApplicationsClocks")]
pub fn reset_applications_clocks(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceResetApplicationsClocks.as_ref())?;
unsafe { nvml_try(sym(self.device)) }
}
/**
Try to set the current state of auto boosted clocks on this `Device`.
Auto boosted clocks are enabled by default on some hardware, allowing the GPU to run
as fast as thermals will allow it to. Auto boosted clocks should be disabled if fixed
clock rates are desired.
On Pascal and newer hardware, auto boosted clocks are controlled through application
clocks. Use `.set_applications_clocks()` and `.reset_applications_clocks()` to control
auto boost behavior.
Non-root users may use this API by default, but access can be restricted by root using
`.set_api_restriction()`.
Note: persistence mode is required to modify the curent auto boost settings and
therefore must be enabled.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support auto boosted clocks
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
Not sure why nothing is said about `NoPermission`.
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetAutoBoostedClocksEnabled")]
pub fn set_auto_boosted_clocks(&mut self, enabled: bool) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetAutoBoostedClocksEnabled.as_ref())?;
unsafe { nvml_try(sym(self.device, state_from_bool(enabled))) }
}
/**
Sets the ideal affinity for the calling thread and `Device` based on the guidelines given in
`.cpu_affinity()`.
Currently supports up to 64 processors.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested (no-run)
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceSetCpuAffinity")]
pub fn set_cpu_affinity(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetCpuAffinity.as_ref())?;
unsafe { nvml_try(sym(self.device)) }
}
/**
Try to set the default state of auto boosted clocks on this `Device`.
This is the default state that auto boosted clocks will return to when no compute
processes (e.g. CUDA application with an active context) are running.
Requires root/admin permissions.
Auto boosted clocks are enabled by default on some hardware, allowing the GPU to run
as fast as thermals will allow it to. Auto boosted clocks should be disabled if fixed
clock rates are desired.
On Pascal and newer hardware, auto boosted clocks are controlled through application
clocks. Use `.set_applications_clocks()` and `.reset_applications_clocks()` to control
auto boost behavior.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NoPermission`, if the calling user does not have permission to change the default state
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support auto boosted clocks
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler or newer non-GeForce fully supported devices and Maxwell or newer
GeForce devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetDefaultAutoBoostedClocksEnabled")]
pub fn set_auto_boosted_clocks_default(&mut self, enabled: bool) -> Result<(), NvmlError> {
let sym = nvml_sym(
self.nvml
.lib
.nvmlDeviceSetDefaultAutoBoostedClocksEnabled
.as_ref(),
)?;
unsafe {
// Passing 0 because NVIDIA says flags are not supported yet
nvml_try(sym(self.device, state_from_bool(enabled), 0))
}
}
/**
Reads the infoROM from this `Device`'s flash and verifies the checksum.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `CorruptedInfoROM`, if this `Device`'s infoROM is corrupted
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
Not sure why `InvalidArg` is not mentioned.
# Device Support
Supports all devices with an infoROM.
*/
// Checked against local
// Tested on machines other than my own
#[doc(alias = "nvmlDeviceValidateInforom")]
pub fn validate_info_rom(&self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceValidateInforom.as_ref())?;
unsafe { nvml_try(sym(self.device)) }
}
// Wrappers for things from Accounting Statistics now
/**
Clears accounting information about all processes that have already terminated.
Requires root/admin permissions.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceClearAccountingPids")]
pub fn clear_accounting_pids(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceClearAccountingPids.as_ref())?;
unsafe { nvml_try(sym(self.device)) }
}
/**
Gets the number of processes that the circular buffer with accounting PIDs can hold
(in number of elements).
This is the max number of processes that accounting information will be stored for
before the oldest process information will get overwritten by information
about new processes.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature or accounting mode
is disabled
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetAccountingBufferSize")]
pub fn accounting_buffer_size(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetAccountingBufferSize.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(self.device, &mut count))?;
Ok(count)
}
}
/**
Gets whether or not per-process accounting mode is enabled.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetAccountingMode")]
pub fn is_accounting_enabled(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetAccountingMode.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device, &mut state))?;
bool_from_state(state)
}
}
/**
Gets the list of processes that can be queried for accounting stats.
The list of processes returned can be in running or terminated state. Note that
in the case of a PID collision some processes might not be accessible before
the circular buffer is full.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature or accounting
mode is disabled
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetAccountingPids")]
pub fn accounting_pids(&self) -> Result, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetAccountingPids.as_ref())?;
unsafe {
let mut count = match self.accounting_pids_count()? {
0 => return Ok(vec![]),
value => value,
};
let mut pids: Vec = vec![mem::zeroed(); count as usize];
nvml_try(sym(self.device, &mut count, pids.as_mut_ptr()))?;
Ok(pids)
}
}
// Helper function for the above.
fn accounting_pids_count(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetAccountingPids.as_ref())?;
unsafe {
// Indicates that we want the count
let mut count: c_uint = 0;
// Null also indicates that we want the count
match sym(self.device, &mut count, ptr::null_mut()) {
// List is empty
nvmlReturn_enum_NVML_SUCCESS => Ok(0),
// Count is set to pids count
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => Ok(count),
// We know this is an error
other => nvml_try(other).map(|_| 0),
}
}
}
/**
Gets a process's accounting stats.
Accounting stats capture GPU utilization and other statistics across the lifetime
of a process. Accounting stats can be queried during the lifetime of the process
and after its termination. The `time` field in `AccountingStats` is reported as
zero during the lifetime of the process and updated to the actual running time
after its termination.
Accounting stats are kept in a circular buffer; newly created processes overwrite
information regarding old processes.
Note:
* Accounting mode needs to be on. See `.is_accounting_enabled()`.
* Only compute and graphics applications stats can be queried. Monitoring
applications can't be queried since they don't contribute to GPU utilization.
* If a PID collision occurs, the stats of the latest process (the one that
terminated last) will be reported.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotFound`, if the process stats were not found
* `NotSupported`, if this `Device` does not support this feature or accounting
mode is disabled
* `Unknown`, on any unexpected error
# Device Support
Suports Kepler and newer fully supported devices.
# Warning
On Kepler devices, per-process stats are accurate _only if_ there's one process
running on this `Device`.
*/
// Checked against local
// Tested (for error)
#[doc(alias = "nvmlDeviceGetAccountingStats")]
pub fn accounting_stats_for(&self, process_id: u32) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetAccountingStats.as_ref())?;
unsafe {
let mut stats: nvmlAccountingStats_t = mem::zeroed();
nvml_try(sym(self.device, process_id, &mut stats))?;
Ok(stats.into())
}
}
/**
Enables or disables per-process accounting.
Requires root/admin permissions.
Note:
* This setting is not persistent and will default to disabled after the driver
unloads. Enable persistence mode to be sure the setting doesn't switch off
to disabled.
* Enabling accounting mode has no negative impact on GPU performance.
* Disabling accounting clears accounting information for all PIDs
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetAccountingMode")]
pub fn set_accounting(&mut self, enabled: bool) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetAccountingMode.as_ref())?;
unsafe { nvml_try(sym(self.device, state_from_bool(enabled))) }
}
// Device commands starting here
/**
Clears the ECC error and other memory error counts for this `Device`.
Sets all of the specified ECC counters to 0, including both detailed and total counts.
This operation takes effect immediately.
Requires root/admin permissions and ECC mode to be enabled.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid or `counter_type` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices. Only applicable to devices with
ECC. Requires `InfoRom::ECC` version 2.0 or higher to clear aggregate
location-based ECC counts. Requires `InfoRom::ECC` version 1.0 or higher to
clear all other ECC counts.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceClearEccErrorCounts")]
pub fn clear_ecc_error_counts(&mut self, counter_type: EccCounter) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceClearEccErrorCounts.as_ref())?;
unsafe { nvml_try(sym(self.device, counter_type.as_c())) }
}
/**
Changes the root/admin restrictions on certain APIs.
This method can be used by a root/admin user to give non root/admin users access
to certain otherwise-restricted APIs. The new setting lasts for the lifetime of
the NVIDIA driver; it is not persistent. See `.is_api_restricted()` to query
current settings.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid or `api_type` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support changing API restrictions or
this `Device` does not support the feature that API restrictions are being set for
(e.g. enabling/disabling auto boosted clocks is not supported by this `Device`).
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetAPIRestriction")]
pub fn set_api_restricted(&mut self, api_type: Api, restricted: bool) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetAPIRestriction.as_ref())?;
unsafe {
nvml_try(sym(
self.device,
api_type.as_c(),
state_from_bool(restricted),
))
}
}
/**
Sets clocks that applications will lock to.
Sets the clocks that compute and graphics applications will be running at. e.g.
CUDA driver requests these clocks during context creation which means this
property defines clocks at which CUDA applications will be running unless some
overspec event occurs (e.g. over power, over thermal or external HW brake).
Can be used as a setting to request constant performance. Requires root/admin
permissions.
On Pascal and newer hardware, this will automatically disable automatic boosting
of clocks. On K80 and newer Kepler and Maxwell GPUs, users desiring fixed performance
should also call `.set_auto_boosted_clocks(false)` to prevent clocks from automatically
boosting above the clock value being set here.
You can determine valid `mem_clock` and `graphics_clock` arg values via
[`Self::supported_memory_clocks()`] and [`Self::supported_graphics_clocks()`].
Note that after a system reboot or driver reload applications clocks go back
to their default value.
See also [`Self::set_mem_locked_clocks()`].
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid or the clocks are not a valid combo
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer non-GeForce fully supported devices and Maxwell or newer
GeForce devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetApplicationsClocks")]
pub fn set_applications_clocks(
&mut self,
mem_clock: u32,
graphics_clock: u32,
) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetApplicationsClocks.as_ref())?;
unsafe { nvml_try(sym(self.device, mem_clock, graphics_clock)) }
}
/**
Sets the compute mode for this `Device`.
The compute mode determines whether a GPU can be used for compute operations
and whether it can be shared across contexts.
This operation takes effect immediately. Under Linux it is not persistent
across reboots and always resets to `Default`. Under Windows it is
persistent.
Under Windows, compute mode may only be set to `Default` when running in WDDM
(physical display connected).
Requires root/admin permissions.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid or `mode` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetComputeMode")]
pub fn set_compute_mode(&mut self, mode: ComputeMode) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetComputeMode.as_ref())?;
unsafe { nvml_try(sym(self.device, mode.as_c())) }
}
/**
Sets the driver model for this `Device`.
This operation takes effect after the next reboot. The model may only be
set to WDDM when running in DEFAULT compute mode. Changing the model to
WDDM is not supported when the GPU doesn't support graphics acceleration
or will not support it after a reboot.
On Windows platforms the device driver can run in either WDDM or WDM (TCC)
mode. If a physical display is attached to a device it must run in WDDM mode.
It is possible to force the change to WDM (TCC) while the display is still
attached with a `Behavior` of `FORCE`. This should only be done if the host
is subsequently powered down and the display is detached from this `Device`
before the next reboot.
Requires root/admin permissions.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid or `model` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
# Platform Support
Only supports Windows.
# Examples
```no_run
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn test() -> Result<(), NvmlError> {
# let nvml = Nvml::init()?;
# let mut device = nvml.device_by_index(0)?;
use nvml_wrapper::bitmasks::Behavior;
use nvml_wrapper::enum_wrappers::device::DriverModel;
device.set_driver_model(DriverModel::WDM, Behavior::DEFAULT)?;
// Force the change to WDM (TCC)
device.set_driver_model(DriverModel::WDM, Behavior::FORCE)?;
# Ok(())
# }
```
*/
// Checked against local
// Tested (no-run)
#[cfg(target_os = "windows")]
#[doc(alias = "nvmlDeviceSetDriverModel")]
pub fn set_driver_model(
&mut self,
model: DriverModel,
flags: Behavior,
) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetDriverModel.as_ref())?;
unsafe { nvml_try(sym(self.device, model.as_c(), flags.bits())) }
}
/**
Lock this `Device`'s clocks to a specific frequency range.
This setting supercedes application clock values and takes effect regardless
of whether or not any CUDA apps are running. It can be used to request constant
performance.
After a system reboot or a driver reload the clocks go back to their default
values.
Requires root/admin permissions.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the provided minimum and maximum clocks are not a valid combo
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Volta and newer fully supported devices.
*/
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetGpuLockedClocks")]
pub fn set_gpu_locked_clocks(
&mut self,
setting: GpuLockedClocksSetting,
) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetGpuLockedClocks.as_ref())?;
let (min_clock_mhz, max_clock_mhz) = setting.into_min_and_max_clocks();
unsafe { nvml_try(sym(self.device, min_clock_mhz, max_clock_mhz)) }
}
/**
Reset this [`Device`]'s clocks to their default values.
This resets to the same values that would be used after a reboot or driver
reload (defaults to idle clocks but can be configured via
[`Self::set_applications_clocks()`]).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Volta and newer fully supported devices.
*/
// Tested (no-run)
#[doc(alias = "nvmlDeviceResetGpuLockedClocks")]
pub fn reset_gpu_locked_clocks(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceResetGpuLockedClocks.as_ref())?;
unsafe { nvml_try(sym(self.device)) }
}
/**
Lock this [`Device`]'s memory clocks to a specific frequency range.
This setting supercedes application clock values and takes effect regardless
of whether or not any CUDA apps are running. It can be used to request
constant performance. See also [`Self::set_applications_clocks()`].
After a system reboot or a driver reload the clocks go back to their default
values. See also [`Self::reset_mem_locked_clocks()`].
You can use [`Self::supported_memory_clocks()`] to determine valid
frequency combinations to pass into this call.
# Device Support
Supports Ampere and newer fully supported devices.
*/
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetMemoryLockedClocks")]
pub fn set_mem_locked_clocks(
&mut self,
min_clock_mhz: u32,
max_clock_mhz: u32,
) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetMemoryLockedClocks.as_ref())?;
unsafe { nvml_try(sym(self.device, min_clock_mhz, max_clock_mhz)) }
}
/**
Reset this [`Device`]'s memory clocks to their default values.
This resets to the same values that would be used after a reboot or driver
reload (defaults to idle clocks but can be configured via
[`Self::set_applications_clocks()`]).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Ampere and newer fully supported devices.
*/
// Tested (no-run)
#[doc(alias = "nvmlDeviceResetMemoryLockedClocks")]
pub fn reset_mem_locked_clocks(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceResetMemoryLockedClocks.as_ref())?;
unsafe { nvml_try(sym(self.device)) }
}
/**
Set whether or not ECC mode is enabled for this `Device`.
Requires root/admin permissions. Only applicable to devices with ECC.
This operation takes effect after the next reboot.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Kepler and newer fully supported devices. Requires `InfoRom::ECC` version
1.0 or higher.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetEccMode")]
pub fn set_ecc(&mut self, enabled: bool) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetEccMode.as_ref())?;
unsafe { nvml_try(sym(self.device, state_from_bool(enabled))) }
}
/**
Sets the GPU operation mode for this `Device`.
Requires root/admin permissions. Changing GOMs requires a reboot, a requirement
that may be removed in the future.
Compute only GOMs don't support graphics acceleration. Under Windows switching
to these GOMs when the pending driver model is WDDM (physical display attached)
is not supported.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid or `mode` is invalid (shouldn't occur?)
* `NotSupported`, if this `Device` does not support GOMs or a specific mode
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports GK110 M-class and X-class Tesla products from the Kepler family. Modes
`LowDP` and `AllOn` are supported on fully supported GeForce products. Not
supported on Quadro and Tesla C-class products.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetGpuOperationMode")]
pub fn set_gpu_op_mode(&mut self, mode: OperationMode) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetGpuOperationMode.as_ref())?;
unsafe { nvml_try(sym(self.device, mode.as_c())) }
}
/**
Sets the persistence mode for this `Device`.
The persistence mode determines whether the GPU driver software is torn down
after the last client exits.
This operation takes effect immediately and requires root/admin permissions.
It is not persistent across reboots; after each reboot it will default to
disabled.
Note that after disabling persistence on a device that has its own NUMA
memory, this `Device` handle will no longer be valid, and to continue to
interact with the physical device that it represents you will need to
obtain a new `Device` using the methods available on the `Nvml` struct.
This limitation is currently only applicable to devices that have a
coherent NVLink connection to system memory.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid
* `NotSupported`, if this `Device` does not support this feature
* `NoPermission`, if the user doesn't have permission to perform this operation
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested (no-run)
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceSetPersistenceMode")]
pub fn set_persistent(&mut self, enabled: bool) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetPersistenceMode.as_ref())?;
unsafe { nvml_try(sym(self.device, state_from_bool(enabled))) }
}
/**
Sets the power limit for this `Device`, in milliwatts.
This limit is not persistent across reboots or driver unloads. Enable
persistent mode to prevent the driver from unloading when no application
is using this `Device`.
Requires root/admin permissions. See `.power_management_limit_constraints()`
to check the allowed range of values.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `Device` is invalid or `limit` is out of range
* `NotSupported`, if this `Device` does not support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
For some reason NVIDIA does not mention `NoPermission`.
# Device Support
Supports Kepler and newer fully supported devices.
*/
// Checked against local
// Tested (no-run)
#[doc(alias = "nvmlDeviceSetPowerManagementLimit")]
pub fn set_power_management_limit(&mut self, limit: u32) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceSetPowerManagementLimit.as_ref())?;
unsafe { nvml_try(sym(self.device, limit)) }
}
// Event handling methods
/**
Starts recording the given `EventTypes` for this `Device` and adding them
to the specified `EventSet`.
Use `.supported_event_types()` to find out which events you can register for
this `Device`.
**Unfortunately, due to the way `error-chain` works, there is no way to
return the set if it is still valid after an error has occured with the
register call.** The set that you passed in will be freed if any error
occurs and will not be returned to you. This is not desired behavior
and I will fix it as soon as it is possible to do so.
All events that occurred before this call was made will not be recorded.
ECC events are only available on `Device`s with ECC enabled. Power capping events
are only available on `Device`s with power management enabled.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if `events` is invalid (shouldn't occur?)
* `NotSupported`, if the platform does not support this feature or some of the
requested event types.
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error. **If this error is returned, the `set` you
passed in has had its resources freed and will not be returned to you**. NVIDIA's
docs say that this error means that the set is in an invalid state.
# Device Support
Supports Fermi and newer fully supported devices.
# Platform Support
Only supports Linux.
# Examples
```
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlErrorWithSource> {
# let nvml = Nvml::init()?;
# let device = nvml.device_by_index(0)?;
use nvml_wrapper::bitmasks::event::EventTypes;
let set = nvml.create_event_set()?;
/*
Register both `CLOCK_CHANGE` and `PSTATE_CHANGE`.
`let set = ...` is a quick way to re-bind the set to the same variable, since
`.register_events()` consumes the set in order to enforce safety and returns it
if everything went well. It does *not* require `set` to be mutable as nothing
is being mutated.
*/
let set = device.register_events(
EventTypes::CLOCK_CHANGE |
EventTypes::PSTATE_CHANGE,
set
)?;
# Ok(())
# }
```
*/
// Checked against local
// Tested
// Thanks to Thinkofname for helping resolve lifetime issues
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceRegisterEvents")]
pub fn register_events(
&self,
events: EventTypes,
set: EventSet<'nvml>,
) -> Result, NvmlErrorWithSource> {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceRegisterEvents.as_ref())?;
unsafe {
match nvml_try(sym(self.device, events.bits(), set.handle())) {
Ok(()) => Ok(set),
Err(NvmlError::Unknown) => {
// NVIDIA says that if an Unknown error is returned, `set` will
// be in an undefined state and should be freed.
if let Err(e) = set.release_events() {
return Err(NvmlErrorWithSource {
error: NvmlError::SetReleaseFailed,
source: Some(e),
});
}
Err(NvmlError::Unknown.into())
}
Err(e) => {
// TODO: return set here so you can use it again?
if let Err(e) = set.release_events() {
return Err(NvmlErrorWithSource {
error: NvmlError::SetReleaseFailed,
source: Some(e),
});
}
Err(e.into())
}
}
}
}
/**
Gets the `EventTypes` that this `Device` supports.
The returned bitmask is created via the `EventTypes::from_bits_truncate`
method, meaning that any bits that don't correspond to flags present in this
version of the wrapper will be dropped.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
# Platform Support
Only supports Linux.
# Examples
```
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlError> {
# let nvml = Nvml::init()?;
# let device = nvml.device_by_index(0)?;
use nvml_wrapper::bitmasks::event::EventTypes;
let supported = device.supported_event_types()?;
if supported.contains(EventTypes::CLOCK_CHANGE) {
println!("The `CLOCK_CHANGE` event is supported.");
} else if supported.contains(
EventTypes::SINGLE_BIT_ECC_ERROR |
EventTypes::DOUBLE_BIT_ECC_ERROR
) {
println!("All ECC error event types are supported.");
}
# Ok(())
# }
```
*/
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetSupportedEventTypes")]
pub fn supported_event_types(&self) -> Result {
Ok(EventTypes::from_bits_truncate(
self.supported_event_types_raw()?,
))
}
/**
Gets the `EventTypes` that this `Device` supports, erroring if any bits
correspond to non-present flags.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `IncorrectBits`, if NVML returns any bits that do not correspond to flags in
`EventTypes`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
# Platform Support
Only supports Linux.
*/
// Tested
#[cfg(target_os = "linux")]
pub fn supported_event_types_strict(&self) -> Result {
let ev_types = self.supported_event_types_raw()?;
EventTypes::from_bits(ev_types).ok_or(NvmlError::IncorrectBits(Bits::U64(ev_types)))
}
// Helper for the above methods.
#[cfg(target_os = "linux")]
fn supported_event_types_raw(&self) -> Result {
let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetSupportedEventTypes.as_ref())?;
unsafe {
let mut ev_types: c_ulonglong = mem::zeroed();
nvml_try(sym(self.device, &mut ev_types))?;
Ok(ev_types)
}
}
// Drain states
/**
Enable or disable drain state for this `Device`.
If you pass `None` as `pci_info`, `.pci_info()` will be called in order to obtain
`PciInfo` to be used within this method.
Enabling drain state forces this `Device` to no longer accept new incoming requests.
Any new NVML processes will no longer see this `Device`.
Must be called as administrator. Persistence mode for this `Device` must be turned
off before this call is made.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` doesn't support this feature
* `NoPermission`, if the calling process has insufficient permissions to perform
this operation
* `InUse`, if this `Device` has persistence mode turned on
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
In addition, all of the errors returned by:
* `.pci_info()`
* `PciInfo.try_into()`
# Device Support
Supports Pascal and newer fully supported devices.
Some Kepler devices are also supported (that's all NVIDIA says, no specifics).
# Platform Support
Only supports Linux.
# Examples
```no_run
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn test() -> Result<(), NvmlError> {
# let nvml = Nvml::init()?;
# let mut device = nvml.device_by_index(0)?;
// Pass `None`, `.set_drain()` call will grab `PciInfo` for us
device.set_drain(true, None)?;
let pci_info = device.pci_info()?;
// Pass in our own `PciInfo`, call will use it instead
device.set_drain(true, pci_info)?;
# Ok(())
# }
```
*/
// Checked against local
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceModifyDrainState")]
pub fn set_drain>>(
&mut self,
enabled: bool,
pci_info: T,
) -> Result<(), NvmlError> {
let pci_info = if let Some(info) = pci_info.into() {
info
} else {
self.pci_info()?
};
let sym = nvml_sym(self.nvml.lib.nvmlDeviceModifyDrainState.as_ref())?;
unsafe { nvml_try(sym(&mut pci_info.try_into()?, state_from_bool(enabled))) }
}
/**
Query the drain state of this `Device`.
If you pass `None` as `pci_info`, `.pci_info()` will be called in order to obtain
`PciInfo` to be used within this method.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` doesn't support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
In addition, all of the errors returned by:
* `.pci_info()`
* `PciInfo.try_into()`
# Device Support
Supports Pascal and newer fully supported devices.
Some Kepler devices are also supported (that's all NVIDIA says, no specifics).
# Platform Support
Only supports Linux.
# Examples
```
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlError> {
# let nvml = Nvml::init()?;
# let mut device = nvml.device_by_index(0)?;
// Pass `None`, `.is_drain_enabled()` call will grab `PciInfo` for us
device.is_drain_enabled(None)?;
let pci_info = device.pci_info()?;
// Pass in our own `PciInfo`, call will use it instead
device.is_drain_enabled(pci_info)?;
# Ok(())
# }
```
*/
// Checked against local
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceQueryDrainState")]
pub fn is_drain_enabled>>(
&self,
pci_info: T,
) -> Result {
let pci_info = if let Some(info) = pci_info.into() {
info
} else {
self.pci_info()?
};
let sym = nvml_sym(self.nvml.lib.nvmlDeviceQueryDrainState.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(&mut pci_info.try_into()?, &mut state))?;
bool_from_state(state)
}
}
/**
Removes this `Device` from the view of both NVML and the NVIDIA kernel driver.
If you pass `None` as `pci_info`, `.pci_info()` will be called in order to obtain
`PciInfo` to be used within this method.
This call only works if no other processes are attached. If other processes
are attached when this is called, the `InUse` error will be returned and
this `Device` will return to its original draining state. The only situation
where this can occur is if a process was and is still using this `Device`
before the call to `set_drain()` was made and it was enabled. Note that
persistence mode counts as an attachment to this `Device` and thus must be
disabled prior to this call.
For long-running NVML processes, please note that this will change the
enumeration of current GPUs. As an example, if there are four GPUs present
and the first is removed, the new enumeration will be 0-2. Device handles
for the removed GPU will be invalid.
NVIDIA doesn't provide much documentation about the `gpu_state` and `link_state`
parameters, so you're on your own there. It does say that the `gpu_state`
controls whether or not this `Device` should be removed from the kernel.
Must be run as administrator.
# Bad Ergonomics Explanation
Previously the design of `error-chain` made it impossible to return stuff
with generic lifetime parameters. The crate's errors are now based on
`std::error::Error`, so this situation no longer needs to be, but I haven't
made time to re-work it.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `NotSupported`, if this `Device` doesn't support this feature
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `InUse`, if this `Device` is still in use and cannot be removed
In addition, all of the errors returned by:
* `.pci_info()`
* `PciInfo.try_into()`
# Device Support
Supports Pascal and newer fully supported devices.
Some Kepler devices are also supported (that's all NVIDIA says, no specifics).
# Platform Support
Only supports Linux.
# Examples
How to handle error case:
```no_run
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# use nvml_wrapper::enum_wrappers::device::{DetachGpuState, PcieLinkState};
# fn test() -> Result<(), NvmlError> {
# let nvml = Nvml::init()?;
# let mut device = nvml.device_by_index(0)?;
match device.remove(None, DetachGpuState::Remove, PcieLinkState::ShutDown) {
(Ok(()), None) => println!("Successful call, `Device` removed"),
(Err(e), Some(d)) => println!("Unsuccessful call. `Device`: {:?}", d),
_ => println!("Something else",)
}
# Ok(())
# }
```
Demonstration of the `pci_info` parameter's use:
```no_run
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# use nvml_wrapper::enum_wrappers::device::{DetachGpuState, PcieLinkState};
# fn test() -> Result<(), NvmlErrorWithSource> {
# let nvml = Nvml::init()?;
# let mut device = nvml.device_by_index(0)?;
// Pass `None`, `.remove()` call will grab `PciInfo` for us
device.remove(None, DetachGpuState::Remove, PcieLinkState::ShutDown).0?;
# let mut device2 = nvml.device_by_index(0)?;
// Different `Device` because `.remove()` consumes the `Device`
let pci_info = device2.pci_info()?;
// Pass in our own `PciInfo`, call will use it instead
device2.remove(pci_info, DetachGpuState::Remove, PcieLinkState::ShutDown).0?;
# Ok(())
# }
```
*/
// Checked against local
// TODO: Fix ergonomics here when possible.
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceRemoveGpu_v2")]
pub fn remove>>(
self,
pci_info: T,
gpu_state: DetachGpuState,
link_state: PcieLinkState,
) -> (Result<(), NvmlErrorWithSource>, Option>) {
let pci_info = if let Some(info) = pci_info.into() {
info
} else {
match self.pci_info() {
Ok(info) => info,
Err(error) => {
return (
Err(NvmlErrorWithSource {
error,
source: Some(NvmlError::GetPciInfoFailed),
}),
Some(self),
)
}
}
};
let mut raw_pci_info = match pci_info.try_into() {
Ok(info) => info,
Err(error) => {
return (
Err(NvmlErrorWithSource {
error,
source: Some(NvmlError::PciInfoToCFailed),
}),
Some(self),
)
}
};
let sym = match nvml_sym(self.nvml.lib.nvmlDeviceRemoveGpu_v2.as_ref()) {
Ok(sym) => sym,
Err(error) => {
return (
Err(NvmlErrorWithSource {
error,
source: None,
}),
Some(self),
)
}
};
unsafe {
match nvml_try(sym(&mut raw_pci_info, gpu_state.as_c(), link_state.as_c())) {
// `Device` removed; call was successful, no `Device` to return
Ok(()) => (Ok(()), None),
// `Device` has not been removed; unsuccessful call, return `Device`
Err(e) => (Err(e.into()), Some(self)),
}
}
}
// NvLink
/**
Obtain a struct that represents an NvLink.
NVIDIA does not provide any information as to how to obtain a valid NvLink
value, so you're on your own there.
*/
pub fn link_wrapper_for(&self, link: u32) -> NvLink {
NvLink { device: self, link }
}
}
#[cfg(test)]
#[deny(unused_mut)]
mod test {
#[cfg(target_os = "linux")]
use crate::bitmasks::event::*;
#[cfg(target_os = "windows")]
use crate::bitmasks::Behavior;
use crate::enum_wrappers::device::*;
use crate::enums::device::GpuLockedClocksSetting;
use crate::error::*;
use crate::structs::device::FieldId;
use crate::sys_exports::field_id::*;
use crate::test_utils::*;
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
#[cfg(target_os = "linux")]
fn clear_cpu_affinity() {
let nvml = nvml();
let mut device = device(&nvml);
device.clear_cpu_affinity().unwrap();
}
#[test]
#[ignore = "my machine does not support this call"]
fn is_api_restricted() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.is_api_restricted(Api::ApplicationClocks)?;
device.is_api_restricted(Api::AutoBoostedClocks)
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn applications_clock() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let gfx_clock = device.applications_clock(Clock::Graphics)?;
let sm_clock = device.applications_clock(Clock::SM)?;
let mem_clock = device.applications_clock(Clock::Memory)?;
let vid_clock = device.applications_clock(Clock::Video)?;
Ok(format!(
"Graphics Clock: {}, SM Clock: {}, Memory Clock: {}, Video Clock: {}",
gfx_clock, sm_clock, mem_clock, vid_clock
))
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn auto_boosted_clocks_enabled() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.auto_boosted_clocks_enabled())
}
#[test]
fn bar1_memory_info() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.bar1_memory_info())
}
#[test]
fn board_id() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.board_id())
}
#[test]
fn brand() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.brand())
}
#[test]
#[ignore = "my machine does not support this call"]
fn bridge_chip_info() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.bridge_chip_info())
}
#[test]
#[ignore = "my machine does not support this call"]
fn clock() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.clock(Clock::Graphics, ClockId::Current)?;
device.clock(Clock::SM, ClockId::TargetAppClock)?;
device.clock(Clock::Memory, ClockId::DefaultAppClock)?;
device.clock(Clock::Video, ClockId::TargetAppClock)
// My machine does not support CustomerMaxBoost
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn max_customer_boost_clock() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.max_customer_boost_clock(Clock::Graphics)?;
device.max_customer_boost_clock(Clock::SM)?;
device.max_customer_boost_clock(Clock::Memory)?;
device.max_customer_boost_clock(Clock::Video)
})
}
#[test]
fn compute_mode() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.compute_mode())
}
#[test]
fn clock_info() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let gfx_clock = device.clock_info(Clock::Graphics)?;
let sm_clock = device.clock_info(Clock::SM)?;
let mem_clock = device.clock_info(Clock::Memory)?;
let vid_clock = device.clock_info(Clock::Video)?;
Ok(format!(
"Graphics Clock: {}, SM Clock: {}, Memory Clock: {}, Video Clock: {}",
gfx_clock, sm_clock, mem_clock, vid_clock
))
})
}
#[test]
fn running_compute_processes() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.running_compute_processes())
}
#[cfg(feature = "legacy-functions")]
#[cfg_attr(feature = "legacy-functions", test)]
fn running_compute_processes_v2() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.running_compute_processes_v2())
}
#[cfg(target_os = "linux")]
#[test]
fn cpu_affinity() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.cpu_affinity(64))
}
#[test]
fn current_pcie_link_gen() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.current_pcie_link_gen())
}
#[test]
fn current_pcie_link_width() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.current_pcie_link_width())
}
#[test]
fn decoder_utilization() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.decoder_utilization())
}
#[test]
#[ignore = "my machine does not support this call"]
fn default_applications_clock() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let gfx_clock = device.default_applications_clock(Clock::Graphics)?;
let sm_clock = device.default_applications_clock(Clock::SM)?;
let mem_clock = device.default_applications_clock(Clock::Memory)?;
let vid_clock = device.default_applications_clock(Clock::Video)?;
Ok(format!(
"Graphics Clock: {}, SM Clock: {}, Memory Clock: {}, Video Clock: {}",
gfx_clock, sm_clock, mem_clock, vid_clock
))
})
}
#[test]
fn is_display_active() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.is_display_active())
}
#[test]
fn is_display_connected() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.is_display_connected())
}
#[cfg(target_os = "windows")]
#[test]
fn driver_model() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.driver_model())
}
#[test]
#[ignore = "my machine does not support this call"]
fn is_ecc_enabled() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.is_ecc_enabled())
}
#[test]
fn encoder_utilization() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.encoder_utilization())
}
#[test]
fn encoder_capacity() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.encoder_capacity(EncoderType::H264)
})
}
#[test]
fn encoder_stats() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.encoder_stats())
}
#[test]
fn encoder_sessions() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.encoder_sessions())
}
#[test]
fn fbc_stats() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.fbc_stats())
}
#[test]
fn fbc_sessions_info() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.fbc_sessions_info())
}
#[test]
fn enforced_power_limit() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.enforced_power_limit())
}
#[test]
fn fan_speed() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.fan_speed(0))
}
#[test]
fn num_fans() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.num_fans())
}
#[test]
#[ignore = "my machine does not support this call"]
fn gpu_operation_mode() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.gpu_operation_mode())
}
#[test]
fn running_graphics_processes() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.running_graphics_processes())
}
#[cfg(feature = "legacy-functions")]
#[cfg_attr(feature = "legacy-functions", test)]
fn running_graphics_processes_v2() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.running_graphics_processes_v2())
}
#[test]
fn process_utilization_stats() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.process_utilization_stats(None))
}
#[test]
fn index() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.index())
}
#[test]
#[ignore = "my machine does not support this call"]
fn config_checksum() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.config_checksum())
}
#[test]
#[ignore = "my machine does not support this call"]
fn info_rom_image_version() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.info_rom_image_version())
}
#[test]
#[ignore = "my machine does not support this call"]
fn info_rom_version() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.info_rom_version(InfoRom::OEM)?;
device.info_rom_version(InfoRom::ECC)?;
device.info_rom_version(InfoRom::Power)
})
}
#[test]
fn max_clock_info() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let gfx_clock = device.max_clock_info(Clock::Graphics)?;
let sm_clock = device.max_clock_info(Clock::SM)?;
let mem_clock = device.max_clock_info(Clock::Memory)?;
let vid_clock = device.max_clock_info(Clock::Video)?;
Ok(format!(
"Graphics Clock: {}, SM Clock: {}, Memory Clock: {}, Video Clock: {}",
gfx_clock, sm_clock, mem_clock, vid_clock
))
})
}
#[test]
fn max_pcie_link_gen() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.max_pcie_link_gen())
}
#[test]
fn max_pcie_link_width() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.max_pcie_link_width())
}
#[test]
#[ignore = "my machine does not support this call"]
fn memory_error_counter() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.memory_error_counter(
MemoryError::Corrected,
EccCounter::Volatile,
MemoryLocation::Device,
)
})
}
#[test]
fn memory_info() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.memory_info())
}
#[cfg(target_os = "linux")]
#[test]
fn minor_number() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.minor_number())
}
#[test]
fn is_multi_gpu_board() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.is_multi_gpu_board())
}
#[test]
fn name() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.name())
}
#[test]
fn pci_info() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.pci_info())
}
#[test]
fn pcie_replay_counter() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.pcie_replay_counter())
}
#[test]
fn pcie_throughput() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.pcie_throughput(PcieUtilCounter::Send)?;
device.pcie_throughput(PcieUtilCounter::Receive)
})
}
#[test]
fn performance_state() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.performance_state())
}
#[cfg(target_os = "linux")]
#[test]
fn is_in_persistent_mode() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.is_in_persistent_mode())
}
#[test]
fn power_management_limit_default() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.power_management_limit_default())
}
#[test]
fn power_management_limit() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.power_management_limit())
}
#[test]
fn power_management_limit_constraints() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.power_management_limit_constraints()
})
}
#[test]
fn is_power_management_algo_active() {
let nvml = nvml();
#[allow(deprecated)]
test_with_device(3, &nvml, |device| device.is_power_management_algo_active())
}
#[test]
fn power_state() {
let nvml = nvml();
#[allow(deprecated)]
test_with_device(3, &nvml, |device| device.power_state())
}
#[test]
fn power_usage() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.power_usage())
}
#[test]
#[ignore = "my machine does not support this call"]
fn retired_pages() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.retired_pages(RetirementCause::MultipleSingleBitEccErrors)?;
device.retired_pages(RetirementCause::DoubleBitEccError)
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn are_pages_pending_retired() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.are_pages_pending_retired())
}
#[test]
#[ignore = "my machine does not support this call"]
fn samples() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.samples(Sampling::ProcessorClock, None)?;
Ok(())
})
}
#[test]
fn field_values_for() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.field_values_for(&[
FieldId(NVML_FI_DEV_ECC_CURRENT),
FieldId(NVML_FI_DEV_ECC_PENDING),
FieldId(NVML_FI_DEV_ECC_SBE_VOL_TOTAL),
FieldId(NVML_FI_DEV_ECC_DBE_VOL_TOTAL),
FieldId(NVML_FI_DEV_ECC_SBE_AGG_TOTAL),
FieldId(NVML_FI_DEV_ECC_DBE_AGG_TOTAL),
FieldId(NVML_FI_DEV_ECC_SBE_VOL_L1),
FieldId(NVML_FI_DEV_ECC_DBE_VOL_L1),
FieldId(NVML_FI_DEV_ECC_SBE_VOL_L2),
FieldId(NVML_FI_DEV_ECC_DBE_VOL_L2),
FieldId(NVML_FI_DEV_ECC_SBE_VOL_DEV),
FieldId(NVML_FI_DEV_ECC_DBE_VOL_DEV),
FieldId(NVML_FI_DEV_ECC_SBE_VOL_REG),
FieldId(NVML_FI_DEV_ECC_DBE_VOL_REG),
FieldId(NVML_FI_DEV_ECC_SBE_VOL_TEX),
FieldId(NVML_FI_DEV_ECC_DBE_VOL_TEX),
FieldId(NVML_FI_DEV_ECC_DBE_VOL_CBU),
FieldId(NVML_FI_DEV_ECC_SBE_AGG_L1),
FieldId(NVML_FI_DEV_ECC_DBE_AGG_L1),
FieldId(NVML_FI_DEV_ECC_SBE_AGG_L2),
FieldId(NVML_FI_DEV_ECC_DBE_AGG_L2),
FieldId(NVML_FI_DEV_ECC_SBE_AGG_DEV),
FieldId(NVML_FI_DEV_ECC_DBE_AGG_DEV),
FieldId(NVML_FI_DEV_ECC_SBE_AGG_REG),
FieldId(NVML_FI_DEV_ECC_DBE_AGG_REG),
FieldId(NVML_FI_DEV_ECC_SBE_AGG_TEX),
FieldId(NVML_FI_DEV_ECC_DBE_AGG_TEX),
FieldId(NVML_FI_DEV_ECC_DBE_AGG_CBU),
FieldId(NVML_FI_DEV_PERF_POLICY_POWER),
FieldId(NVML_FI_DEV_PERF_POLICY_THERMAL),
FieldId(NVML_FI_DEV_PERF_POLICY_SYNC_BOOST),
FieldId(NVML_FI_DEV_PERF_POLICY_BOARD_LIMIT),
FieldId(NVML_FI_DEV_PERF_POLICY_LOW_UTILIZATION),
FieldId(NVML_FI_DEV_PERF_POLICY_RELIABILITY),
FieldId(NVML_FI_DEV_PERF_POLICY_TOTAL_APP_CLOCKS),
FieldId(NVML_FI_DEV_PERF_POLICY_TOTAL_BASE_CLOCKS),
FieldId(NVML_FI_DEV_MEMORY_TEMP),
FieldId(NVML_FI_DEV_TOTAL_ENERGY_CONSUMPTION),
])
})
}
// Passing an empty slice should return an `InvalidArg` error
#[should_panic(expected = "InvalidArg")]
#[test]
fn field_values_for_empty() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.field_values_for(&[]))
}
#[test]
#[ignore = "my machine does not support this call"]
fn serial() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.serial())
}
#[test]
#[ignore = "my machine does not support this call"]
fn board_part_number() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.board_part_number())
}
#[test]
fn current_throttle_reasons() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.current_throttle_reasons())
}
#[test]
fn current_throttle_reasons_strict() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.current_throttle_reasons_strict())
}
#[test]
fn supported_throttle_reasons() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.supported_throttle_reasons())
}
#[test]
fn supported_throttle_reasons_strict() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.supported_throttle_reasons_strict()
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn supported_graphics_clocks() {
let nvml = nvml();
#[allow(unused_variables)]
test_with_device(3, &nvml, |device| {
let supported = device.supported_graphics_clocks(810)?;
Ok(())
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn supported_memory_clocks() {
let nvml = nvml();
#[allow(unused_variables)]
test_with_device(3, &nvml, |device| {
let supported = device.supported_memory_clocks()?;
Ok(())
})
}
#[test]
fn temperature() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.temperature(TemperatureSensor::Gpu)
})
}
#[test]
fn temperature_threshold() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let slowdown = device.temperature_threshold(TemperatureThreshold::Slowdown)?;
let shutdown = device.temperature_threshold(TemperatureThreshold::Shutdown)?;
Ok((slowdown, shutdown))
})
}
// I do not have 2 devices
#[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");
device1
.topology_common_ancestor(device2)
.expect("TopologyLevel");
}
#[cfg(target_os = "linux")]
#[test]
fn topology_nearest_gpus() {
let nvml = nvml();
let device = device(&nvml);
test(3, || device.topology_nearest_gpus(TopologyLevel::System))
}
#[test]
#[ignore = "my machine does not support this call"]
fn total_ecc_errors() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.total_ecc_errors(MemoryError::Corrected, EccCounter::Volatile)
})
}
#[test]
fn uuid() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.uuid())
}
#[test]
fn utilization_rates() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.utilization_rates())
}
#[test]
fn vbios_version() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.vbios_version())
}
#[test]
fn violation_status() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
device.violation_status(PerformancePolicy::Power)
})
}
#[test]
fn num_cores() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.num_cores())
}
#[test]
fn irq_num() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.irq_num())
}
#[test]
fn power_source() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.power_source())
}
#[test]
fn memory_bus_width() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.memory_bus_width())
}
#[test]
fn pcie_link_max_speed() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.max_pcie_link_speed())
}
#[test]
fn bus_type() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.bus_type())
}
#[test]
fn architecture() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.architecture())
}
// I do not have 2 devices
#[ignore = "my machine does not support this call"]
#[test]
fn is_on_same_board_as() {
let nvml = nvml();
let device1 = device(&nvml);
let device2 = nvml.device_by_index(1).expect("device");
device1.is_on_same_board_as(&device2).expect("bool");
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn reset_applications_clocks() {
let nvml = nvml();
let mut device = device(&nvml);
device.reset_applications_clocks().expect("reset clocks")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_auto_boosted_clocks() {
let nvml = nvml();
let mut device = device(&nvml);
device.set_auto_boosted_clocks(true).expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
#[cfg(target_os = "linux")]
fn set_cpu_affinity() {
let nvml = nvml();
let mut device = device(&nvml);
device.set_cpu_affinity().expect("ideal affinity set")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_auto_boosted_clocks_default() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_auto_boosted_clocks_default(true)
.expect("set to true")
}
#[test]
#[ignore = "my machine does not support this call"]
fn validate_info_rom() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.validate_info_rom())
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn clear_accounting_pids() {
let nvml = nvml();
let mut device = device(&nvml);
device.clear_accounting_pids().expect("cleared")
}
#[test]
fn accounting_buffer_size() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.accounting_buffer_size())
}
#[test]
fn is_accounting_enabled() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.is_accounting_enabled())
}
#[test]
fn accounting_pids() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.accounting_pids())
}
#[should_panic(expected = "NotFound")]
#[test]
fn accounting_stats_for() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let processes = device.running_graphics_processes()?;
// We never enable accounting mode, so this should return a `NotFound` error
match device.accounting_stats_for(processes[0].pid) {
Err(NvmlError::NotFound) => panic!("NotFound"),
other => other,
}
})
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_accounting() {
let nvml = nvml();
let mut device = device(&nvml);
device.set_accounting(true).expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn clear_ecc_error_counts() {
let nvml = nvml();
let mut device = device(&nvml);
device
.clear_ecc_error_counts(EccCounter::Aggregate)
.expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_api_restricted() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_api_restricted(Api::ApplicationClocks, true)
.expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_applications_clocks() {
let nvml = nvml();
let mut device = device(&nvml);
device.set_applications_clocks(32, 32).expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_compute_mode() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_compute_mode(ComputeMode::Default)
.expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[cfg(target_os = "windows")]
#[allow(dead_code)]
fn set_driver_model() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_driver_model(DriverModel::WDM, Behavior::DEFAULT)
.expect("set to wdm")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_gpu_locked_clocks() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_gpu_locked_clocks(GpuLockedClocksSetting::Numeric {
min_clock_mhz: 1048,
max_clock_mhz: 1139,
})
.expect("set to a range")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn reset_gpu_locked_clocks() {
let nvml = nvml();
let mut device = device(&nvml);
device.reset_gpu_locked_clocks().expect("clocks reset")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_mem_locked_clocks() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_mem_locked_clocks(1048, 1139)
.expect("set to a range")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn reset_mem_locked_clocks() {
let nvml = nvml();
let mut device = device(&nvml);
device.reset_mem_locked_clocks().expect("clocks reset")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_ecc() {
let nvml = nvml();
let mut device = device(&nvml);
device.set_ecc(true).expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_gpu_op_mode() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_gpu_op_mode(OperationMode::AllOn)
.expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
#[cfg(target_os = "linux")]
fn set_persistent() {
let nvml = nvml();
let mut device = device(&nvml);
device.set_persistent(true).expect("set to true")
}
// This modifies device state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_power_management_limit() {
let nvml = nvml();
let mut device = device(&nvml);
device
.set_power_management_limit(250000)
.expect("set to true")
}
#[cfg(target_os = "linux")]
#[allow(unused_variables)]
#[test]
fn register_events() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let set = nvml.create_event_set()?;
let set = device
.register_events(
EventTypes::PSTATE_CHANGE
| EventTypes::CRITICAL_XID_ERROR
| EventTypes::CLOCK_CHANGE,
set,
)
.map_err(|e| e.error)?;
Ok(())
})
}
#[cfg(target_os = "linux")]
#[test]
fn supported_event_types() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.supported_event_types())
}
#[cfg(target_os = "linux")]
#[test]
fn supported_event_types_strict() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.supported_event_types_strict())
}
#[cfg(target_os = "linux")]
#[test]
fn is_drain_enabled() {
let nvml = nvml();
test_with_device(3, &nvml, |device| device.is_drain_enabled(None))
}
}
nvml-wrapper-0.10.0/src/enum_wrappers/device.rs 0000644 0000000 0000000 00000052753 00726746425 0017663 0 ustar 0000000 0000000 use crate::error::NvmlError;
use crate::ffi::bindings::*;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use wrapcenum_derive::EnumWrapper;
/// API types that allow changes to default permission restrictions.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlRestrictedAPI_enum")]
pub enum Api {
/**
APIs that change application clocks.
Applicable methods on `Device`: `.set_applications_clocks()`,
`.reset_applications_clocks()`
*/
#[wrap(c_variant = "NVML_RESTRICTED_API_SET_APPLICATION_CLOCKS")]
ApplicationClocks,
/// APIs that enable/disable auto boosted clocks.
///
/// Applicable methods on `Device`: `.set_auto_boosted_clocks()`
#[wrap(c_variant = "NVML_RESTRICTED_API_SET_AUTO_BOOSTED_CLOCKS")]
AutoBoostedClocks,
}
/// Clock types. All speeds are in MHz.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlClockType_enum")]
pub enum Clock {
/// Graphics clock domain.
#[wrap(c_variant = "NVML_CLOCK_GRAPHICS")]
Graphics,
/// SM (Streaming Multiprocessor) clock domain.
///
/// What AMD calls a CU (Compute Unit) can be compared to this.
#[wrap(c_variant = "NVML_CLOCK_SM")]
SM,
/// Memory clock domain.
#[wrap(c_variant = "NVML_CLOCK_MEM")]
Memory,
/// Video encoder/decoder clock domain.
#[wrap(c_variant = "NVML_CLOCK_VIDEO")]
Video,
}
/// These are used in combo with `Clock` to specify a single clock value.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlClockId_enum")]
pub enum ClockId {
/// Current actual clock value.
#[wrap(c_variant = "NVML_CLOCK_ID_CURRENT")]
Current,
/// Target application clock.
#[wrap(c_variant = "NVML_CLOCK_ID_APP_CLOCK_TARGET")]
TargetAppClock,
/// Default application clock target.
#[wrap(c_variant = "NVML_CLOCK_ID_APP_CLOCK_DEFAULT")]
DefaultAppClock,
/// OEM-defined maximum clock rate.
#[wrap(c_variant = "NVML_CLOCK_ID_CUSTOMER_BOOST_MAX")]
CustomerMaxBoost,
}
/// GPU brand.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlBrandType_enum")]
pub enum Brand {
#[wrap(c_variant = "NVML_BRAND_UNKNOWN")]
Unknown,
/// Targeted at workstations.
#[wrap(c_variant = "NVML_BRAND_QUADRO")]
Quadro,
/// Targeted at high-end compute.
#[wrap(c_variant = "NVML_BRAND_TESLA")]
Tesla,
/// NVIDIA's multi-display cards.
#[wrap(c_variant = "NVML_BRAND_NVS")]
NVS,
/// Targeted at virtualization (vGPUs).
///
/// Deprecated from API reporting, still here for backwards compatibility.
#[wrap(c_variant = "NVML_BRAND_GRID")]
GRID,
/// Targeted at gaming.
#[wrap(c_variant = "NVML_BRAND_GEFORCE")]
GeForce,
/// Targeted at... people who don't quite need quadros?
#[wrap(c_variant = "NVML_BRAND_TITAN")]
Titan,
/// Targeted at virtualized apps.
#[wrap(c_variant = "NVML_BRAND_NVIDIA_VAPPS")]
VApps,
/// Targeted at virtualized pcs.
#[wrap(c_variant = "NVML_BRAND_NVIDIA_VPC")]
VPC,
/// Targeted at virtualized servers.
#[wrap(c_variant = "NVML_BRAND_NVIDIA_VCS")]
VCS,
/// Targeted at virtualized work stations.
#[wrap(c_variant = "NVML_BRAND_NVIDIA_VWS")]
VWS,
/// Targeted at cloud gaming servers.
#[wrap(c_variant = "NVML_BRAND_NVIDIA_CLOUD_GAMING")]
CloudGaming,
/// Deprecated from API reporting, still here for backwards compatibility.
/// Symlinks to [`Brand::CloudGaming`].
#[wrap(c_variant = "NVML_BRAND_NVIDIA_VGAMING")]
VGaming,
/// Targeted at Quadro RTX cards.
#[wrap(c_variant = "NVML_BRAND_QUADRO_RTX")]
QuadroRTX,
/// Targeted at RTX cards.
#[wrap(c_variant = "NVML_BRAND_NVIDIA_RTX")]
NvidiaRTX,
/// Nvidia
#[wrap(c_variant = "NVML_BRAND_NVIDIA")]
Nvidia,
/// Targeted at gaming RTX cards.
#[wrap(c_variant = "NVML_BRAND_GEFORCE_RTX")]
GeForceRTX,
/// Targeted at Titan RTX cards.
#[wrap(c_variant = "NVML_BRAND_TITAN_RTX")]
TitanRTX,
}
/**
Represents type of a bridge chip.
NVIDIA does not provide docs (in the code, that is) explaining what each chip
type is, so you're on your own there.
*/
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlBridgeChipType_enum")]
pub enum BridgeChip {
#[wrap(c_variant = "NVML_BRIDGE_CHIP_PLX")]
PLX,
#[wrap(c_variant = "NVML_BRIDGE_CHIP_BRO4")]
BRO4,
}
/// Memory error types.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlMemoryErrorType_enum")]
pub enum MemoryError {
/**
A memory error that was corrected.
ECC error: single bit error.
Texture memory: error fixed by a resend.
*/
#[wrap(c_variant = "NVML_MEMORY_ERROR_TYPE_CORRECTED")]
Corrected,
/**
A memory error that was not corrected.
ECC error: double bit error.
Texture memory: error occurred and resend failed.
*/
#[wrap(c_variant = "NVML_MEMORY_ERROR_TYPE_UNCORRECTED")]
Uncorrected,
}
/**
ECC counter types.
Note: Volatile counts are reset each time the driver loads. On Windows this is
once per boot. On Linux this can be more frequent; the driver unloads when no
active clients exist. If persistence mode is enabled or there is always a
driver client active (such as X11), then Linux also sees per-boot behavior.
If not, volatile counts are reset each time a compute app is run.
*/
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlEccCounterType_enum")]
pub enum EccCounter {
/// Volatile counts are reset each time the driver loads.
#[wrap(c_variant = "NVML_VOLATILE_ECC")]
Volatile,
/// Aggregate counts persist across reboots (i.e. for the lifetime of the
/// device).
#[wrap(c_variant = "NVML_AGGREGATE_ECC")]
Aggregate,
}
/// Memory locations. See `Device.memory_error_counter()`.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlMemoryLocation_enum")]
pub enum MemoryLocation {
/// GPU L1 cache.
#[wrap(c_variant = "NVML_MEMORY_LOCATION_L1_CACHE")]
L1Cache,
/// GPU L2 cache.
#[wrap(c_variant = "NVML_MEMORY_LOCATION_L2_CACHE")]
L2Cache,
/// GPU device memory.
#[wrap(c_variant = "NVML_MEMORY_LOCATION_DEVICE_MEMORY")]
Device,
/// GPU register file.
#[wrap(c_variant = "NVML_MEMORY_LOCATION_REGISTER_FILE")]
RegisterFile,
/// GPU texture memory.
#[wrap(c_variant = "NVML_MEMORY_LOCATION_TEXTURE_MEMORY")]
Texture,
/// Shared memory.
#[wrap(c_variant = "NVML_MEMORY_LOCATION_TEXTURE_SHM")]
Shared,
#[wrap(c_variant = "NVML_MEMORY_LOCATION_CBU")]
Cbu,
/// SRAM present on Turing and above.
#[wrap(c_variant = "NVML_MEMORY_LOCATION_SRAM")]
SRAM,
}
/// Driver models, Windows only.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlDriverModel_enum")]
#[cfg(target_os = "windows")]
pub enum DriverModel {
/// GPU treated as a display device.
#[wrap(c_variant = "NVML_DRIVER_WDDM")]
WDDM,
/// (TCC model) GPU treated as a generic device (recommended).
#[wrap(c_variant = "NVML_DRIVER_WDM")]
WDM,
}
/**
GPU operation mode.
Allows for the reduction of power usage and optimization of GPU throughput
by disabling GPU features. Each mode is designed to meet specific needs.
*/
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlGom_enum")]
pub enum OperationMode {
/// Everything is enabled and running at full speed.
#[wrap(c_variant = "NVML_GOM_ALL_ON")]
AllOn,
/// Designed for running only compute tasks; disables graphics operations.
#[wrap(c_variant = "NVML_GOM_COMPUTE")]
Compute,
/// Designed for running graphics applications that don't require high
/// bandwidth double precision.
#[wrap(c_variant = "NVML_GOM_LOW_DP")]
LowDP,
}
/// Available infoROM objects.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlInforomObject_enum")]
pub enum InfoRom {
/// An object defined by OEM.
#[wrap(c_variant = "NVML_INFOROM_OEM")]
OEM,
/// The ECC object determining the level of ECC support.
#[wrap(c_variant = "NVML_INFOROM_ECC")]
ECC,
/// The power management object.
#[wrap(c_variant = "NVML_INFOROM_POWER")]
Power,
}
/// Represents the queryable PCIe utilization counters (in bytes). 1KB
/// granularity.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlPcieUtilCounter_enum")]
pub enum PcieUtilCounter {
#[wrap(c_variant = "NVML_PCIE_UTIL_TX_BYTES")]
Send,
#[wrap(c_variant = "NVML_PCIE_UTIL_RX_BYTES")]
Receive,
}
/**
Allowed performance states.
```text
Value Performance
0 (highest)
...
15 (lowest)
```
*/
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlPStates_enum")]
pub enum PerformanceState {
/// Maximum performance.
#[wrap(c_variant = "NVML_PSTATE_0")]
Zero,
#[wrap(c_variant = "NVML_PSTATE_1")]
One,
#[wrap(c_variant = "NVML_PSTATE_2")]
Two,
#[wrap(c_variant = "NVML_PSTATE_3")]
Three,
#[wrap(c_variant = "NVML_PSTATE_4")]
Four,
#[wrap(c_variant = "NVML_PSTATE_5")]
Five,
#[wrap(c_variant = "NVML_PSTATE_6")]
Six,
#[wrap(c_variant = "NVML_PSTATE_7")]
Seven,
#[wrap(c_variant = "NVML_PSTATE_8")]
Eight,
#[wrap(c_variant = "NVML_PSTATE_9")]
Nine,
#[wrap(c_variant = "NVML_PSTATE_10")]
Ten,
#[wrap(c_variant = "NVML_PSTATE_11")]
Eleven,
#[wrap(c_variant = "NVML_PSTATE_12")]
Twelve,
#[wrap(c_variant = "NVML_PSTATE_13")]
Thirteen,
#[wrap(c_variant = "NVML_PSTATE_14")]
Fourteen,
/// Minimum peformance.
#[wrap(c_variant = "NVML_PSTATE_15")]
Fifteen,
/// Unknown performance state.
#[wrap(c_variant = "NVML_PSTATE_UNKNOWN")]
Unknown,
}
/// Causes for page retirement.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlPageRetirementCause_enum")]
pub enum RetirementCause {
/// Page was retired due to multiple single bit ECC errors.
#[wrap(c_variant = "NVML_PAGE_RETIREMENT_CAUSE_MULTIPLE_SINGLE_BIT_ECC_ERRORS")]
MultipleSingleBitEccErrors,
/// Page was retired due to a single double bit ECC error.
#[wrap(c_variant = "NVML_PAGE_RETIREMENT_CAUSE_DOUBLE_BIT_ECC_ERROR")]
DoubleBitEccError,
}
/// Possible types of sampling events.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlSamplingType_enum")]
pub enum Sampling {
/// Total power drawn by GPU.
#[wrap(c_variant = "NVML_TOTAL_POWER_SAMPLES")]
Power,
/// Percent of time during which one or more kernels was executing on the
/// GPU.
#[wrap(c_variant = "NVML_GPU_UTILIZATION_SAMPLES")]
GpuUtilization,
/// Percent of time during which global (device) memory was being read or
/// written.
#[wrap(c_variant = "NVML_MEMORY_UTILIZATION_SAMPLES")]
MemoryUtilization,
/// Percent of time during which NVENC remains busy.
#[wrap(c_variant = "NVML_ENC_UTILIZATION_SAMPLES")]
EncoderUtilization,
/// Percent of time during which NVDEC remains busy.
#[wrap(c_variant = "NVML_DEC_UTILIZATION_SAMPLES")]
DecoderUtilization,
/// Processor clock samples.
#[wrap(c_variant = "NVML_PROCESSOR_CLK_SAMPLES")]
ProcessorClock,
/// Memory clock samples.
#[wrap(c_variant = "NVML_MEMORY_CLK_SAMPLES")]
MemoryClock,
}
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlTemperatureSensors_enum")]
pub enum TemperatureSensor {
/// Sensor for the GPU die.
#[wrap(c_variant = "NVML_TEMPERATURE_GPU")]
Gpu,
}
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlTemperatureThresholds_enum")]
pub enum TemperatureThreshold {
/// Temperature at which the GPU will shut down for hardware protection.
#[wrap(c_variant = "NVML_TEMPERATURE_THRESHOLD_SHUTDOWN")]
Shutdown,
/// Temperature at which the GPU will begin hardware throttling.
#[wrap(c_variant = "NVML_TEMPERATURE_THRESHOLD_SLOWDOWN")]
Slowdown,
/// Memory temperature at which the GPU will begin software slowdown.
#[wrap(c_variant = "NVML_TEMPERATURE_THRESHOLD_MEM_MAX")]
MemoryMax,
/// GPU temperature at which the GPU can be throttled below the base clock.
#[wrap(c_variant = "NVML_TEMPERATURE_THRESHOLD_GPU_MAX")]
GpuMax,
}
/// Level relationships within a system between two GPUs.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlGpuLevel_enum")]
pub enum TopologyLevel {
/// e.g. Tesla K80.
#[wrap(c_variant = "NVML_TOPOLOGY_INTERNAL")]
Internal,
/// All devices that only need traverse a single PCIe switch.
#[wrap(c_variant = "NVML_TOPOLOGY_SINGLE")]
Single,
/// All devices that need not traverse a host bridge.
#[wrap(c_variant = "NVML_TOPOLOGY_MULTIPLE")]
Multiple,
/// All devices that are connected to the same host bridge.
#[wrap(c_variant = "NVML_TOPOLOGY_HOSTBRIDGE")]
HostBridge,
/**
All devices that are connected to the same NUMA node but possibly
multiple host bridges.
This was `Cpu` in previous versions of NVML.
*/
#[wrap(c_variant = "NVML_TOPOLOGY_NODE")]
Node,
/// All devices in the system
#[wrap(c_variant = "NVML_TOPOLOGY_SYSTEM")]
System,
}
/// Types of performance policy for which violation times can be queried.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlPerfPolicyType_enum")]
pub enum PerformancePolicy {
#[wrap(c_variant = "NVML_PERF_POLICY_POWER")]
Power,
#[wrap(c_variant = "NVML_PERF_POLICY_THERMAL")]
Thermal,
#[wrap(c_variant = "NVML_PERF_POLICY_SYNC_BOOST")]
SyncBoost,
#[wrap(c_variant = "NVML_PERF_POLICY_BOARD_LIMIT")]
BoardLimit,
#[wrap(c_variant = "NVML_PERF_POLICY_LOW_UTILIZATION")]
LowUtilization,
/// Board reliability limit.
#[wrap(c_variant = "NVML_PERF_POLICY_RELIABILITY")]
Reliability,
/// Total time the GPU was limited by any of the above.
#[wrap(c_variant = "NVML_PERF_POLICY_TOTAL_APP_CLOCKS")]
TotalAppClocks,
/// Total time the GPU was held below base clocks.
#[wrap(c_variant = "NVML_PERF_POLICY_TOTAL_BASE_CLOCKS")]
TotalBaseClocks,
}
/// `ExclusiveProcess` was added in CUDA 4.0. Earlier CUDA versions supported a
/// single exclusive mode, which is equivalent to `ExclusiveThread` in CUDA 4.0
/// and beyond.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlComputeMode_enum")]
pub enum ComputeMode {
/// Multiple contexts per device.
#[wrap(c_variant = "NVML_COMPUTEMODE_DEFAULT")]
Default,
/// *SUPPORT REMOVED*
///
/// Only one context per device, usable from one thread at a time. *NOT
/// SUPPORTED*
#[wrap(c_variant = "NVML_COMPUTEMODE_EXCLUSIVE_THREAD")]
ExclusiveThread,
/// No contexts per device.
#[wrap(c_variant = "NVML_COMPUTEMODE_PROHIBITED")]
Prohibited,
/// Only one context per device, usable from multiple threads at a time.
#[wrap(c_variant = "NVML_COMPUTEMODE_EXCLUSIVE_PROCESS")]
ExclusiveProcess,
}
/// P2P capability index status.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlGpuP2PStatus_enum")]
pub enum P2pStatus {
#[wrap(c_variant = "NVML_P2P_STATUS_OK")]
Ok,
#[wrap(c_variant = "NVML_P2P_STATUS_CHIPSET_NOT_SUPPORED")]
ChipsetNotSupported,
#[wrap(c_variant = "NVML_P2P_STATUS_GPU_NOT_SUPPORTED")]
GpuNotSupported,
#[wrap(c_variant = "NVML_P2P_STATUS_IOH_TOPOLOGY_NOT_SUPPORTED")]
IohTopologyNotSupported,
#[wrap(c_variant = "NVML_P2P_STATUS_DISABLED_BY_REGKEY")]
DisabledByRegkey,
#[wrap(c_variant = "NVML_P2P_STATUS_NOT_SUPPORTED")]
NotSupported,
#[wrap(c_variant = "NVML_P2P_STATUS_UNKNOWN")]
Unknown,
}
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlGpuP2PCapsIndex_enum")]
pub enum P2pCapabilitiesIndex {
#[wrap(c_variant = "NVML_P2P_CAPS_INDEX_READ")]
Read,
#[wrap(c_variant = "NVML_P2P_CAPS_INDEX_WRITE")]
Write,
#[wrap(c_variant = "NVML_P2P_CAPS_INDEX_NVLINK")]
NvLink,
#[wrap(c_variant = "NVML_P2P_CAPS_INDEX_ATOMICS")]
Atomics,
#[wrap(c_variant = "NVML_P2P_CAPS_INDEX_PROP")]
Prop,
#[wrap(c_variant = "NVML_P2P_CAPS_INDEX_UNKNOWN")]
Unknown,
}
/// Represents types for returned sample values.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlValueType_enum")]
pub enum SampleValueType {
#[wrap(c_variant = "NVML_VALUE_TYPE_DOUBLE")]
Double,
#[wrap(c_variant = "NVML_VALUE_TYPE_UNSIGNED_INT")]
UnsignedInt,
#[wrap(c_variant = "NVML_VALUE_TYPE_UNSIGNED_LONG")]
UnsignedLong,
#[wrap(c_variant = "NVML_VALUE_TYPE_UNSIGNED_LONG_LONG")]
UnsignedLongLong,
#[wrap(c_variant = "NVML_VALUE_TYPE_SIGNED_LONG_LONG")]
SignedLongLong,
}
/// Represents encoder types that capacity can be queried for.
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlEncoderQueryType_enum")]
pub enum EncoderType {
#[wrap(c_variant = "NVML_ENCODER_QUERY_H264")]
H264,
#[wrap(c_variant = "NVML_ENCODER_QUERY_HEVC")]
HEVC,
}
/// The type of a frame buffer capture session
///
/// NVIDIA doesn't document the variants beyond their names.
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlFBCSessionType_enum")]
pub enum FbcSessionType {
#[wrap(c_variant = "NVML_FBC_SESSION_TYPE_UNKNOWN")]
Unknown,
#[wrap(c_variant = "NVML_FBC_SESSION_TYPE_TOSYS")]
ToSys,
#[wrap(c_variant = "NVML_FBC_SESSION_TYPE_CUDA")]
Cuda,
#[wrap(c_variant = "NVML_FBC_SESSION_TYPE_VID")]
Vid,
#[wrap(c_variant = "NVML_FBC_SESSION_TYPE_HWENC")]
HwEnc,
}
/// Options to pass to [`crate::Device::remove()`].
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlDetachGpuState_enum")]
pub enum DetachGpuState {
#[wrap(c_variant = "NVML_DETACH_GPU_KEEP")]
Keep,
#[wrap(c_variant = "NVML_DETACH_GPU_REMOVE")]
Remove,
}
/// Options to pass to [`crate::Device::remove()`].
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlPcieLinkState_enum")]
pub enum PcieLinkState {
#[wrap(c_variant = "NVML_PCIE_LINK_KEEP")]
Keep,
#[wrap(c_variant = "NVML_PCIE_LINK_SHUT_DOWN")]
ShutDown,
}
/// Clock limit IDs for use with [`crate::Device::set_gpu_locked_clocks()`].
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlClockLimitId_enum")]
pub enum ClockLimitId {
/// Bound clock speed by the TDP of the device.
#[wrap(c_variant = "NVML_CLOCK_LIMIT_ID_TDP")]
Tdp,
/// No bound for clock speed.
#[wrap(c_variant = "NVML_CLOCK_LIMIT_ID_UNLIMITED")]
Unlimited,
}
nvml-wrapper-0.10.0/src/enum_wrappers/mod.rs 0000644 0000000 0000000 00000001151 00726746425 0017165 0 ustar 0000000 0000000 use crate::error::NvmlError;
use crate::ffi::bindings::*;
pub mod device;
pub mod nv_link;
pub mod unit;
pub fn bool_from_state(state: nvmlEnableState_t) -> Result {
match state {
nvmlEnableState_enum_NVML_FEATURE_DISABLED => Ok(false),
nvmlEnableState_enum_NVML_FEATURE_ENABLED => Ok(true),
_ => Err(NvmlError::UnexpectedVariant(state)),
}
}
pub fn state_from_bool(enabled: bool) -> nvmlEnableState_t {
if enabled {
nvmlEnableState_enum_NVML_FEATURE_ENABLED
} else {
nvmlEnableState_enum_NVML_FEATURE_DISABLED
}
}
nvml-wrapper-0.10.0/src/enum_wrappers/nv_link.rs 0000644 0000000 0000000 00000004610 00726746425 0020051 0 ustar 0000000 0000000 use crate::error::NvmlError;
use crate::ffi::bindings::*;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use wrapcenum_derive::EnumWrapper;
/// Represents the NvLink utilization counter packet units.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlNvLinkUtilizationCountUnits_enum")]
pub enum UtilizationCountUnit {
#[wrap(c_variant = "NVML_NVLINK_COUNTER_UNIT_CYCLES")]
Cycles,
#[wrap(c_variant = "NVML_NVLINK_COUNTER_UNIT_PACKETS")]
Packets,
#[wrap(c_variant = "NVML_NVLINK_COUNTER_UNIT_BYTES")]
Bytes,
}
/// Represents queryable NvLink capabilities.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlNvLinkCapability_enum")]
pub enum Capability {
/// P2P over NVLink is supported.
#[wrap(c_variant = "NVML_NVLINK_CAP_P2P_SUPPORTED")]
P2p,
/// Access to system memory is supported.
#[wrap(c_variant = "NVML_NVLINK_CAP_SYSMEM_ACCESS")]
SysMemAccess,
/// P2P atomics are supported.
#[wrap(c_variant = "NVML_NVLINK_CAP_P2P_ATOMICS")]
P2pAtomics,
/// System memory atomics are supported.
#[wrap(c_variant = "NVML_NVLINK_CAP_SYSMEM_ATOMICS")]
SysMemAtomics,
/// SLI is supported over this link.
#[wrap(c_variant = "NVML_NVLINK_CAP_SLI_BRIDGE")]
SliBridge,
/// Link is supported on this device.
#[wrap(c_variant = "NVML_NVLINK_CAP_VALID")]
ValidLink,
}
/// Represents queryable NvLink error counters.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlNvLinkErrorCounter_enum")]
pub enum ErrorCounter {
/// Data link transmit replay error counter.
#[wrap(c_variant = "NVML_NVLINK_ERROR_DL_REPLAY")]
DlReplay,
/// Data link transmit recovery error counter.
#[wrap(c_variant = "NVML_NVLINK_ERROR_DL_RECOVERY")]
DlRecovery,
/// Data link receive flow control digit CRC error counter.
#[wrap(c_variant = "NVML_NVLINK_ERROR_DL_CRC_FLIT")]
DlCrcFlit,
/// Data link receive data CRC error counter.
#[wrap(c_variant = "NVML_NVLINK_ERROR_DL_CRC_DATA")]
DlCrcData,
}
nvml-wrapper-0.10.0/src/enum_wrappers/unit.rs 0000644 0000000 0000000 00000001673 00726746425 0017376 0 ustar 0000000 0000000 use crate::error::NvmlError;
use crate::ffi::bindings::*;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use wrapcenum_derive::EnumWrapper;
/// Unit fan state.
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlFanState_enum")]
pub enum FanState {
/// Working properly
#[wrap(c_variant = "NVML_FAN_NORMAL")]
Normal,
#[wrap(c_variant = "NVML_FAN_FAILED")]
Failed,
}
// Checked against local
#[derive(EnumWrapper, Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[wrap(c_enum = "nvmlLedColor_enum")]
pub enum LedColor {
/// Used to indicate good health.
#[wrap(c_variant = "NVML_LED_COLOR_GREEN")]
Green,
/// Used to indicate a problem.
#[wrap(c_variant = "NVML_LED_COLOR_AMBER")]
Amber,
}
nvml-wrapper-0.10.0/src/enums/device.rs 0000644 0000000 0000000 00000030514 00726746425 0016112 0 ustar 0000000 0000000 use std::convert::TryFrom;
use std::fmt::Display;
use std::os::raw::c_uint;
use crate::enum_wrappers::device::{ClockLimitId, SampleValueType};
use crate::error::NvmlError;
use crate::ffi::bindings::*;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
/// Respresents possible variants for a firmware version.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum FirmwareVersion {
/// The version is unavailable.
Unavailable,
Version(u32),
}
impl From for FirmwareVersion {
fn from(value: u32) -> Self {
match value {
0 => FirmwareVersion::Unavailable,
_ => FirmwareVersion::Version(value),
}
}
}
/// Represents possible variants for used GPU memory.
// Checked
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum UsedGpuMemory {
/// Under WDDM, `NVML_VALUE_NOT_AVAILABLE` is always reported because
/// Windows KMD manages all the memory, not the NVIDIA driver.
Unavailable,
/// Memory used in bytes.
Used(u64),
}
impl From for UsedGpuMemory {
fn from(value: u64) -> Self {
let not_available = (NVML_VALUE_NOT_AVAILABLE) as u64;
match value {
v if v == not_available => UsedGpuMemory::Unavailable,
_ => UsedGpuMemory::Used(value),
}
}
}
/// Represents different types of sample values.
// Checked against local
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SampleValue {
F64(f64),
U32(u32),
U64(u64),
I64(i64),
}
impl SampleValue {
pub fn from_tag_and_union(tag: &SampleValueType, union: nvmlValue_t) -> Self {
use self::SampleValueType::*;
unsafe {
match *tag {
Double => SampleValue::F64(union.dVal),
UnsignedInt => SampleValue::U32(union.uiVal),
// Methodology: NVML supports 32-bit Linux. UL is u32 on that platform.
// NVML wouldn't return anything larger
#[allow(clippy::unnecessary_cast)]
UnsignedLong => SampleValue::U32(union.ulVal as u32),
UnsignedLongLong => SampleValue::U64(union.ullVal),
SignedLongLong => SampleValue::I64(union.sllVal),
}
}
}
}
/// Represents different types of sample values.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GpuLockedClocksSetting {
/// Numeric setting that allows you to explicitly define minimum and
/// maximum clock frequencies.
Numeric {
min_clock_mhz: u32,
max_clock_mhz: u32,
},
/// Symbolic setting that allows you to define lower and upper bounds for
/// clock speed with various possibilities.
///
/// Not all combinations of `lower_bound` and `upper_bound` are valid.
/// Please see the docs for `nvmlDeviceSetGpuLockedClocks` in `nvml.h` to
/// learn more.
Symbolic {
lower_bound: ClockLimitId,
upper_bound: ClockLimitId,
},
}
impl GpuLockedClocksSetting {
/// Returns `(min_clock_mhz, max_clock_mhz)`.
pub fn into_min_and_max_clocks(self) -> (u32, u32) {
match self {
GpuLockedClocksSetting::Numeric {
min_clock_mhz,
max_clock_mhz,
} => (min_clock_mhz, max_clock_mhz),
GpuLockedClocksSetting::Symbolic {
lower_bound,
upper_bound,
} => (lower_bound.as_c(), upper_bound.as_c()),
}
}
}
/// Returned by [`crate::Device::bus_type()`].
// TODO: technically this is an "enum wrapper" but the type on the C side isn't
// an enum
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BusType {
/// Unknown bus type.
Unknown,
/// PCI (Peripheral Component Interconnect) bus type.
Pci,
/// PCIE (Peripheral Component Interconnect Express) bus type.
///
/// This is the most common bus type.
Pcie,
/// FPCI (Fast Peripheral Component Interconnect) bus type.
Fpci,
/// AGP (Accelerated Graphics Port) bus type.
///
/// This is old and was dropped in favor of PCIE.
Agp,
}
impl BusType {
/// Returns the C constant equivalent for the given Rust enum variant.
pub fn as_c(&self) -> nvmlBusType_t {
match *self {
Self::Unknown => NVML_BUS_TYPE_UNKNOWN,
Self::Pci => NVML_BUS_TYPE_PCI,
Self::Pcie => NVML_BUS_TYPE_PCIE,
Self::Fpci => NVML_BUS_TYPE_FPCI,
Self::Agp => NVML_BUS_TYPE_AGP,
}
}
}
impl TryFrom for BusType {
type Error = NvmlError;
fn try_from(data: nvmlBusType_t) -> Result {
match data {
NVML_BUS_TYPE_UNKNOWN => Ok(Self::Unknown),
NVML_BUS_TYPE_PCI => Ok(Self::Pci),
NVML_BUS_TYPE_PCIE => Ok(Self::Pcie),
NVML_BUS_TYPE_FPCI => Ok(Self::Fpci),
NVML_BUS_TYPE_AGP => Ok(Self::Agp),
_ => Err(NvmlError::UnexpectedVariant(data)),
}
}
}
/// Returned by [`crate::Device::power_source()`].
// TODO: technically this is an "enum wrapper" but the type on the C side isn't
// an enum
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PowerSource {
/// AC power (receiving power from some external source).
Ac,
/// Battery power.
Battery,
}
impl PowerSource {
/// Returns the C constant equivalent for the given Rust enum variant.
pub fn as_c(&self) -> nvmlPowerSource_t {
match *self {
Self::Ac => NVML_POWER_SOURCE_AC,
Self::Battery => NVML_POWER_SOURCE_BATTERY,
}
}
}
impl TryFrom for PowerSource {
type Error = NvmlError;
fn try_from(data: nvmlPowerSource_t) -> Result {
match data {
NVML_POWER_SOURCE_AC => Ok(Self::Ac),
NVML_POWER_SOURCE_BATTERY => Ok(Self::Battery),
_ => Err(NvmlError::UnexpectedVariant(data)),
}
}
}
/// Returned by [`crate::Device::architecture()`].
///
/// This is the simplified chip architecture of the device.
// TODO: technically this is an "enum wrapper" but the type on the C side isn't
// an enum
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum DeviceArchitecture {
///
Kepler,
///
Maxwell,
///
Pascal,
///
Volta,
///
Turing,
///
Ampere,
///
Ada,
///
Hopper,
/// Unknown device architecture (most likely something newer).
Unknown,
}
impl DeviceArchitecture {
/// Returns the C constant equivalent for the given Rust enum variant.
pub fn as_c(&self) -> nvmlDeviceArchitecture_t {
match *self {
Self::Kepler => NVML_DEVICE_ARCH_KEPLER,
Self::Maxwell => NVML_DEVICE_ARCH_MAXWELL,
Self::Pascal => NVML_DEVICE_ARCH_PASCAL,
Self::Volta => NVML_DEVICE_ARCH_VOLTA,
Self::Turing => NVML_DEVICE_ARCH_TURING,
Self::Ampere => NVML_DEVICE_ARCH_AMPERE,
Self::Ada => NVML_DEVICE_ARCH_ADA,
Self::Hopper => NVML_DEVICE_ARCH_HOPPER,
Self::Unknown => NVML_DEVICE_ARCH_UNKNOWN,
}
}
}
impl TryFrom for DeviceArchitecture {
type Error = NvmlError;
fn try_from(data: nvmlDeviceArchitecture_t) -> Result {
match data {
NVML_DEVICE_ARCH_KEPLER => Ok(Self::Kepler),
NVML_DEVICE_ARCH_MAXWELL => Ok(Self::Maxwell),
NVML_DEVICE_ARCH_PASCAL => Ok(Self::Pascal),
NVML_DEVICE_ARCH_VOLTA => Ok(Self::Volta),
NVML_DEVICE_ARCH_TURING => Ok(Self::Turing),
NVML_DEVICE_ARCH_AMPERE => Ok(Self::Ampere),
NVML_DEVICE_ARCH_ADA => Ok(Self::Ada),
NVML_DEVICE_ARCH_HOPPER => Ok(Self::Hopper),
NVML_DEVICE_ARCH_UNKNOWN => Ok(Self::Unknown),
_ => Err(NvmlError::UnexpectedVariant(data)),
}
}
}
impl Display for DeviceArchitecture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Kepler => f.write_str("Kepler"),
Self::Maxwell => f.write_str("Maxwell"),
Self::Pascal => f.write_str("Pascal"),
Self::Volta => f.write_str("Volta"),
Self::Turing => f.write_str("Turing"),
Self::Ampere => f.write_str("Ampere"),
Self::Ada => f.write_str("Ada"),
Self::Hopper => f.write_str("Hopper"),
Self::Unknown => f.write_str("Unknown"),
}
}
}
/// Returned by [`crate::Device::max_pcie_link_speed()`].
///
/// Note, the NVML header says these are all MBPS (Megabytes Per Second) but
/// they don't line up with the throughput numbers on this page:
///
///
/// They _do_ line up with the "transfer rate per lane" numbers, though. This
/// would mean they represent transfer speeds rather than throughput, in MT/s.
///
/// See also the discussion on [`crate::Device::pcie_link_speed()`].
// TODO: technically this is an "enum wrapper" but the type on the C side isn't
// an enum
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PcieLinkMaxSpeed {
Invalid,
MegaTransfersPerSecond2500,
MegaTransfersPerSecond5000,
MegaTransfersPerSecond8000,
MegaTransfersPerSecond16000,
MegaTransfersPerSecond32000,
}
impl PcieLinkMaxSpeed {
/// Returns the numerical equivalent for the given enum variant, if valid.
pub fn as_integer(&self) -> Option {
Some(match self {
PcieLinkMaxSpeed::Invalid => return None,
PcieLinkMaxSpeed::MegaTransfersPerSecond2500 => 2500,
PcieLinkMaxSpeed::MegaTransfersPerSecond5000 => 5000,
PcieLinkMaxSpeed::MegaTransfersPerSecond8000 => 8000,
PcieLinkMaxSpeed::MegaTransfersPerSecond16000 => 16000,
PcieLinkMaxSpeed::MegaTransfersPerSecond32000 => 32000,
})
}
/// Returns the C constant equivalent for the given Rust enum variant.
pub fn as_c(&self) -> c_uint {
match *self {
Self::Invalid => NVML_PCIE_LINK_MAX_SPEED_INVALID,
Self::MegaTransfersPerSecond2500 => NVML_PCIE_LINK_MAX_SPEED_2500MBPS,
Self::MegaTransfersPerSecond5000 => NVML_PCIE_LINK_MAX_SPEED_5000MBPS,
Self::MegaTransfersPerSecond8000 => NVML_PCIE_LINK_MAX_SPEED_8000MBPS,
Self::MegaTransfersPerSecond16000 => NVML_PCIE_LINK_MAX_SPEED_16000MBPS,
Self::MegaTransfersPerSecond32000 => NVML_PCIE_LINK_MAX_SPEED_32000MBPS,
}
}
}
impl TryFrom for PcieLinkMaxSpeed {
type Error = NvmlError;
fn try_from(data: c_uint) -> Result {
match data {
NVML_PCIE_LINK_MAX_SPEED_INVALID => Ok(Self::Invalid),
NVML_PCIE_LINK_MAX_SPEED_2500MBPS => Ok(Self::MegaTransfersPerSecond2500),
NVML_PCIE_LINK_MAX_SPEED_5000MBPS => Ok(Self::MegaTransfersPerSecond5000),
NVML_PCIE_LINK_MAX_SPEED_8000MBPS => Ok(Self::MegaTransfersPerSecond8000),
NVML_PCIE_LINK_MAX_SPEED_16000MBPS => Ok(Self::MegaTransfersPerSecond16000),
NVML_PCIE_LINK_MAX_SPEED_32000MBPS => Ok(Self::MegaTransfersPerSecond32000),
_ => Err(NvmlError::UnexpectedVariant(data)),
}
}
}
nvml-wrapper-0.10.0/src/enums/event.rs 0000644 0000000 0000000 00000000747 00726746425 0016001 0 ustar 0000000 0000000 #[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
/// A simple wrapper used to encode the `Unknown` value into the type system.
///
/// `Unknown` would otherwise be a value of 999 (if it were not an enum
/// variant).
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum XidError {
/// Contains the value of the error.
Value(u64),
/// If the error is unknown.
Unknown,
}
nvml-wrapper-0.10.0/src/enums/mod.rs 0000644 0000000 0000000 00000000102 00726746425 0015420 0 ustar 0000000 0000000 pub mod device;
pub mod event;
pub mod nv_link;
pub mod unit;
nvml-wrapper-0.10.0/src/enums/nv_link.rs 0000644 0000000 0000000 00000000623 00726746425 0016311 0 ustar 0000000 0000000 #[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
/// Used to specify the counter in `NvLink.set_utilization_control_for()`
///
/// NVIDIA simply says that the counter specified can be either 0 or 1.
#[repr(u32)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Counter {
Zero = 0,
One = 1,
}
nvml-wrapper-0.10.0/src/enums/unit.rs 0000644 0000000 0000000 00000002777 00726746425 0015644 0 ustar 0000000 0000000 use crate::error::NvmlError;
use crate::ffi::bindings::*;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use std::{convert::TryFrom, ffi::CStr};
/// LED states for an S-class unit.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum LedState {
/// Indicates good health.
Green,
/// Indicates a problem along with the accompanying cause.
Amber(String),
}
impl TryFrom for LedState {
type Error = NvmlError;
/**
Construct `LedState` from the corresponding C struct.
# Errors
* `UnexpectedVariant`, for which you can read the docs for
*/
fn try_from(value: nvmlLedState_t) -> Result {
let color = value.color;
match color {
nvmlLedColor_enum_NVML_LED_COLOR_GREEN => Ok(LedState::Green),
nvmlLedColor_enum_NVML_LED_COLOR_AMBER => unsafe {
let cause_raw = CStr::from_ptr(value.cause.as_ptr());
Ok(LedState::Amber(cause_raw.to_str()?.into()))
},
_ => Err(NvmlError::UnexpectedVariant(color)),
}
}
}
/// The type of temperature reading to take for a `Unit`.
///
/// Available readings depend on the product.
#[repr(u32)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TemperatureReading {
Intake = 0,
Exhaust = 1,
Board = 2,
}
nvml-wrapper-0.10.0/src/error.rs 0000644 0000000 0000000 00000015744 00726746425 0014665 0 ustar 0000000 0000000 use crate::ffi::bindings::*;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Bits {
U32(u32),
U64(u64),
}
/// An `NvmlError` with an optionally present source error for chaining errors
#[derive(Error, Debug)]
#[error("{error}")]
pub struct NvmlErrorWithSource {
pub error: NvmlError,
pub source: Option,
}
impl From for NvmlErrorWithSource {
fn from(error: NvmlError) -> Self {
Self {
error,
source: None,
}
}
}
#[derive(Error, Debug)]
pub enum NvmlError {
#[error("could not interpret string as utf-8")]
Utf8Error(#[from] std::str::Utf8Error),
#[error("nul byte inside string")]
NulError(#[from] std::ffi::NulError),
#[error("a libloading error occurred: {0}")]
LibloadingError(#[from] libloading::Error),
/**
A function symbol failed to load.
This variant is constructed with a textual description of a
`libloading::Error`. The error variant itself can't be provided because we're
unable to take ownership of the error when attempting to use a symbol, and
`libloading::Error` doesn't impl `Clone`.
*/
#[error("function symbol failed to load: {0}")]
FailedToLoadSymbol(String),
#[error("max string length was {max_len} but string length is {actual_len}")]
StringTooLong { max_len: usize, actual_len: usize },
#[error("invalid combination of bits ({0:?}) when trying to interpret as bitflags")]
IncorrectBits(Bits),
/**
An unexpected enum variant was encountered.
This error is specific to this Rust wrapper. It is used to represent the
possibility that an enum variant that is not defined within the Rust bindings
can be returned from a C call.
The single field contains the value that could not be mapped to a
defined enum variant.
See [this issue](https://github.com/rust-lang/rust/issues/36927).
*/
#[error("unexpected enum variant value: {0}")]
UnexpectedVariant(u32),
#[error("a call to `EventSet.release_events()` failed")]
SetReleaseFailed,
#[error("a call to `Device.pci_info()` failed")]
GetPciInfoFailed,
#[error("a call to `PciInfo.try_into_c()` failed")]
PciInfoToCFailed,
#[error("NVML was not first initialized with `Nvml::init()`")]
Uninitialized,
#[error("a supplied argument was invalid")]
InvalidArg,
#[error("the requested operation is not available on the target device")]
NotSupported,
#[error("the current user does not have permission to perform this operation")]
NoPermission,
#[error("NVML was already initialized")]
#[deprecated = "deprecated in NVML (multiple initializations now allowed via refcounting)"]
AlreadyInitialized,
#[error("a query to find an object was unsuccessful")]
NotFound,
/**
An input argument is not large enough.
The single field is the size required for a successful call (if `Some`)
and `None` if unknown.
*/
// TODO: verify that ^
#[error(
"an input argument is not large enough{}",
if let Some(size) = .0 {
format!(", needs to be at least {}", size)
} else {
"".into()
}
)]
InsufficientSize(Option),
#[error("device's external power cables are not properly attached")]
InsufficientPower,
#[error("NVIDIA driver is not loaded")]
DriverNotLoaded,
#[error("the provided timeout was reached")]
Timeout,
#[error("NVIDIA kernel detected an interrupt issue with a device")]
IrqIssue,
#[error("a shared library couldn't be found or loaded")]
LibraryNotFound,
#[error("a function couldn't be found in a shared library")]
FunctionNotFound,
#[error("the infoROM is corrupted")]
CorruptedInfoROM,
#[error("device fell off the bus or has otherwise become inacessible")]
GpuLost,
#[error("device requires a reset before it can be used again")]
ResetRequired,
#[error("device control has been blocked by the operating system/cgroups")]
OperatingSystem,
#[error("RM detects a driver/library version mismatch")]
LibRmVersionMismatch,
#[error("operation cannot be performed because the GPU is currently in use")]
InUse,
#[error("insufficient memory")]
InsufficientMemory,
#[error("no data")]
NoData,
#[error(
"the requested vgpu operation is not available on the target device because \
ECC is enabled"
)]
VgpuEccNotSupported,
#[error("an internal driver error occured")]
Unknown,
}
/// Converts an `nvmlReturn_t` type into a `Result<(), NvmlError>`.
#[allow(deprecated)]
pub fn nvml_try(code: nvmlReturn_t) -> Result<(), NvmlError> {
use NvmlError::*;
match code {
nvmlReturn_enum_NVML_SUCCESS => Ok(()),
nvmlReturn_enum_NVML_ERROR_UNINITIALIZED => Err(Uninitialized),
nvmlReturn_enum_NVML_ERROR_INVALID_ARGUMENT => Err(InvalidArg),
nvmlReturn_enum_NVML_ERROR_NOT_SUPPORTED => Err(NotSupported),
nvmlReturn_enum_NVML_ERROR_NO_PERMISSION => Err(NoPermission),
nvmlReturn_enum_NVML_ERROR_ALREADY_INITIALIZED => Err(AlreadyInitialized),
nvmlReturn_enum_NVML_ERROR_NOT_FOUND => Err(NotFound),
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_SIZE => Err(InsufficientSize(None)),
nvmlReturn_enum_NVML_ERROR_INSUFFICIENT_POWER => Err(InsufficientPower),
nvmlReturn_enum_NVML_ERROR_DRIVER_NOT_LOADED => Err(DriverNotLoaded),
nvmlReturn_enum_NVML_ERROR_TIMEOUT => Err(Timeout),
nvmlReturn_enum_NVML_ERROR_IRQ_ISSUE => Err(IrqIssue),
nvmlReturn_enum_NVML_ERROR_LIBRARY_NOT_FOUND => Err(LibraryNotFound),
nvmlReturn_enum_NVML_ERROR_FUNCTION_NOT_FOUND => Err(FunctionNotFound),
nvmlReturn_enum_NVML_ERROR_CORRUPTED_INFOROM => Err(CorruptedInfoROM),
nvmlReturn_enum_NVML_ERROR_GPU_IS_LOST => Err(GpuLost),
nvmlReturn_enum_NVML_ERROR_RESET_REQUIRED => Err(ResetRequired),
nvmlReturn_enum_NVML_ERROR_OPERATING_SYSTEM => Err(OperatingSystem),
nvmlReturn_enum_NVML_ERROR_LIB_RM_VERSION_MISMATCH => Err(LibRmVersionMismatch),
nvmlReturn_enum_NVML_ERROR_IN_USE => Err(InUse),
nvmlReturn_enum_NVML_ERROR_MEMORY => Err(InsufficientMemory),
nvmlReturn_enum_NVML_ERROR_NO_DATA => Err(NoData),
nvmlReturn_enum_NVML_ERROR_VGPU_ECC_NOT_SUPPORTED => Err(VgpuEccNotSupported),
nvmlReturn_enum_NVML_ERROR_UNKNOWN => Err(Unknown),
_ => Err(UnexpectedVariant(code)),
}
}
/// Helper to map a `&libloading::Error` into an `NvmlError`
pub fn nvml_sym<'a, T>(sym: Result<&'a T, &libloading::Error>) -> Result<&'a T, NvmlError> {
sym.map_err(|e| NvmlError::FailedToLoadSymbol(e.to_string()))
}
nvml-wrapper-0.10.0/src/event.rs 0000644 0000000 0000000 00000013636 00726746425 0014653 0 ustar 0000000 0000000 use crate::error::{nvml_sym, nvml_try, NvmlError};
use crate::ffi::bindings::*;
use crate::Nvml;
use std::mem;
use crate::struct_wrappers::event::EventData;
/**
Handle to a set of events.
**Operations on a set are not thread-safe.** It does not, therefore, implement `Sync`.
You can get yourself an `EventSet` via `Nvml.create_event_set`.
Lifetimes are used to enforce that each `EventSet` instance cannot be used after
the `Nvml` instance it was obtained from is dropped:
```compile_fail
use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlError> {
let nvml = Nvml::init()?;
let event_set = nvml.create_event_set()?;
drop(nvml);
// This won't compile
event_set.wait(5)?;
# Ok(())
# }
```
*/
// Checked against local
#[derive(Debug)]
pub struct EventSet<'nvml> {
set: nvmlEventSet_t,
pub nvml: &'nvml Nvml,
}
unsafe impl<'nvml> Send for EventSet<'nvml> {}
impl<'nvml> EventSet<'nvml> {
/**
Create a new `EventSet` wrapper.
You will most likely never need to call this; see the methods available to you
on the `Nvml` struct to get one.
# Safety
It is your responsibility to ensure that the given `nvmlEventSet_t` pointer
is valid.
*/
// TODO: move constructor to this struct?
// Clippy bug, see https://github.com/rust-lang/rust-clippy/issues/5593
#[allow(clippy::missing_safety_doc)]
pub unsafe fn new(set: nvmlEventSet_t, nvml: &'nvml Nvml) -> Self {
Self { set, nvml }
}
/**
Use this to release the set's events if you care about handling
potential errors (*the `Drop` implementation ignores errors!*).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `Unknown`, on any unexpected error
*/
// Checked against local
#[doc(alias = "nvmlEventSetFree")]
pub fn release_events(self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlEventSetFree.as_ref())?;
unsafe {
nvml_try(sym(self.set))?;
}
mem::forget(self);
Ok(())
}
/**
Waits on events for the given timeout (in ms) and delivers one when it arrives.
See the `high_level::event_loop` module for an abstracted version of this.
This method returns immediately if an event is ready to be delivered when it
is called. If no events are ready it will sleep until an event arrives, but
not longer than the specified timeout. In certain conditions, this method
could return before the timeout passes (e.g. when an interrupt arrives).
In the case of an XID error, the function returns the most recent XID error
type seen by the system. If there are multiple XID errors generated before
this method is called, the last seen XID error type will be returned for
all XID error events.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `Timeout`, if no event arrived in the specified timeout or an interrupt
arrived
* `GpuLost`, if a GPU has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
*/
// Checked against local
#[doc(alias = "nvmlEventSetWait_v2")]
pub fn wait(&self, timeout_ms: u32) -> Result, NvmlError> {
let sym = nvml_sym(self.nvml.lib.nvmlEventSetWait_v2.as_ref())?;
unsafe {
let mut data: nvmlEventData_t = mem::zeroed();
nvml_try(sym(self.set, &mut data, timeout_ms))?;
Ok(EventData::new(data, self.nvml))
}
}
/// Get the raw device handle contained in this struct
///
/// Sometimes necessary for C interop.
///
/// # Safety
///
/// This is unsafe to prevent it from being used without care. In
/// particular, you must avoid creating a new `EventSet` from this handle
/// and allowing both this `EventSet` and the newly created one to drop
/// (which would result in a double-free).
pub unsafe fn handle(&self) -> nvmlEventSet_t {
self.set
}
}
/// This `Drop` implementation ignores errors! Use the `.release_events()`
/// method on the `EventSet` struct if you care about handling them.
impl<'nvml> Drop for EventSet<'nvml> {
#[doc(alias = "nvmlEventSetFree")]
fn drop(&mut self) {
unsafe {
self.nvml.lib.nvmlEventSetFree(self.set);
}
}
}
#[cfg(test)]
#[cfg(target_os = "linux")]
mod test {
use crate::bitmasks::event::*;
use crate::test_utils::*;
#[test]
fn release_events() {
let nvml = nvml();
test_with_device(3, &nvml, |device| {
let set = nvml.create_event_set()?;
let set = device
.register_events(
EventTypes::PSTATE_CHANGE
| EventTypes::CRITICAL_XID_ERROR
| EventTypes::CLOCK_CHANGE,
set,
)
.map_err(|e| e.error)?;
set.release_events()
})
}
#[cfg(feature = "test-local")]
#[test]
fn wait() {
use crate::error::NvmlError;
let nvml = nvml();
let device = device(&nvml);
let set = nvml.create_event_set().expect("event set");
let set = device
.register_events(
EventTypes::PSTATE_CHANGE
| EventTypes::CRITICAL_XID_ERROR
| EventTypes::CLOCK_CHANGE,
set,
)
.expect("registration");
let data = match set.wait(10_000) {
Err(NvmlError::Timeout) => return (),
Ok(d) => d,
_ => panic!("An error other than `Timeout` occurred"),
};
print!("{:?} ...", data);
}
}
nvml-wrapper-0.10.0/src/high_level/event_loop.rs 0000644 0000000 0000000 00000020742 00726746425 0020006 0 ustar 0000000 0000000 /*!
A convenient abstraction for working with events.
Simply register the devices you wish to receive events for and then compose
a handler for the events. Event handling looks like this (details removed):
```no_run
# extern crate nvml_wrapper as nvml;
#
# #[cfg(target_os = "linux")]
# fn main() {
# example::actual_main().unwrap();
# }
#
# #[cfg(target_os = "windows")]
# fn main() {}
#
# #[cfg(target_os = "linux")]
# mod example {
# use nvml::Nvml;
# use nvml::error::{NvmlError, NvmlErrorWithSource};
# use nvml::high_level::EventLoopProvider;
# use nvml::high_level::Event::*;
#
# pub fn actual_main() -> Result<(), NvmlErrorWithSource> {
# let nvml = Nvml::init()?;
# let device = nvml.device_by_index(0)?;
# let mut event_loop = nvml.create_event_loop(vec![&device])?;
#
event_loop.run_forever(|event, state| match event {
// If there were no errors, extract the event
Ok(event) => match event {
ClockChange(device) => { /* ... */ },
PowerStateChange(device) => { /* ... */ },
_ => { /* ... */ }
},
// If there was an error, handle it
Err(error) => match error {
// If the error is `Unknown`, continue looping and hope for the best
NvmlError::Unknown => {},
// The other errors that can occur are almost guaranteed to mean that
// further looping will never be successful (`GpuLost` and
// `Uninitialized`), so we stop looping
_ => state.interrupt()
}
});
# Ok(())
# }
# }
```
The full, fleshed-out example can be viewed in the examples directory
(`event_loop.rs`). Run it as follows:
```bash
cargo run --example event_loop
```
The functionality in this module is only available on Linux platforms; NVML does
not support events on any other platform.
*/
use crate::bitmasks::event::EventTypes;
use crate::enums::event::XidError;
use crate::error::{NvmlError, NvmlErrorWithSource};
use crate::struct_wrappers::event::EventData;
use crate::Device;
use crate::EventSet;
use crate::Nvml;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
// TODO: Tests
/**
Represents the event types that an `EventLoop` can gather for you.
These are analagous to the constants in `bitmasks::event`.
Checking to see if the `Device` within an `Event` is the same physical device as
another `Device` that you have on hand can be accomplished via `Device.uuid()`.
*/
#[derive(Debug)]
pub enum Event<'nvml> {
ClockChange(Device<'nvml>),
CriticalXidError(Device<'nvml>, XidError),
DoubleBitEccError(Device<'nvml>),
PowerStateChange(Device<'nvml>),
SingleBitEccError(Device<'nvml>),
/// Returned if none of the other `Events` are contained in the `EventData`
/// the `EventLoop` processes.
Unknown,
}
impl<'nvml> From> for Event<'nvml> {
fn from(struct_: EventData<'nvml>) -> Self {
if struct_.event_type.contains(EventTypes::CLOCK_CHANGE) {
Event::ClockChange(struct_.device)
} else if struct_.event_type.contains(EventTypes::CRITICAL_XID_ERROR) {
// We can unwrap here because we know `event_data` will be `Some`
// since the error is `CRITICAL_XID_ERROR`
Event::CriticalXidError(struct_.device, struct_.event_data.unwrap())
} else if struct_
.event_type
.contains(EventTypes::DOUBLE_BIT_ECC_ERROR)
{
Event::DoubleBitEccError(struct_.device)
} else if struct_.event_type.contains(EventTypes::PSTATE_CHANGE) {
Event::PowerStateChange(struct_.device)
} else if struct_
.event_type
.contains(EventTypes::SINGLE_BIT_ECC_ERROR)
{
Event::SingleBitEccError(struct_.device)
} else {
Event::Unknown
}
}
}
/**
Holds the `EventSet` utilized within an event loop.
A usage example is available (`examples/event_loop.rs`). It can be run as
follows:
```bash
cargo run --example event_loop
```
*/
pub struct EventLoop<'nvml> {
set: EventSet<'nvml>,
}
impl<'nvml> EventLoop<'nvml> {
/**
Register another device that this `EventLoop` should receive events for.
This method takes ownership of this struct and then hands it back to you if
everything went well with the registration process.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `GpuLost`, if a GPU has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Platform Support
Only supports Linux.
*/
pub fn register_device(
mut self,
device: &'nvml Device<'nvml>,
) -> Result {
self.set = device.register_events(device.supported_event_types()?, self.set)?;
Ok(self)
}
/**
Handle events with the given callback until the loop is manually interrupted.
# Errors
The function itself does not return anything. You will be given errors to
handle within your closure if they occur; events are handed to you wrapped
in a `Result`.
The errors that you will need to handle are:
* `Uninitialized`, if the library has not been successfully initialized
* `GpuLost`, if a GPU has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
# Examples
See the `event_loop` example in the `examples` directory at the root.
# Platform Support
Only supports Linux.
*/
pub fn run_forever(&mut self, mut callback: F)
where
F: FnMut(Result, NvmlError>, &mut EventLoopState),
{
let mut state = EventLoopState { interrupted: false };
loop {
if state.interrupted {
break;
};
match self.set.wait(1) {
Ok(data) => {
callback(Ok(data.into()), &mut state);
}
Err(NvmlError::Timeout) => continue,
value => callback(value.map(|d| d.into()), &mut state),
};
}
}
/// Obtain a reference to the `EventSet` contained within this struct.
pub fn as_inner(&'nvml self) -> &'nvml EventSet<'nvml> {
&(self.set)
}
/// Obtain a mutable reference to the `EventSet` contained within this
/// struct.
pub fn as_mut_inner(&'nvml mut self) -> &'nvml mut EventSet<'nvml> {
&mut (self.set)
}
/// Consumes this `EventLoop` and yields the `EventSet` contained within.
pub fn into_inner(self) -> EventSet<'nvml> {
self.set
}
}
impl<'nvml> From> for EventLoop<'nvml> {
fn from(set: EventSet<'nvml>) -> Self {
Self { set }
}
}
/// Keeps track of whether an `EventLoop` is interrupted or not.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EventLoopState {
interrupted: bool,
}
impl EventLoopState {
/// Call this to mark the loop as interrupted.
pub fn interrupt(&mut self) {
self.interrupted = true;
}
}
/// Adds a method to obtain an `EventLoop` to the `Nvml` struct.
///
/// `use` it at your leisure.
pub trait EventLoopProvider {
// Thanks to Thinkofname for lifetime help, again :)
fn create_event_loop<'nvml>(
&'nvml self,
devices: Vec<&'nvml Device<'nvml>>,
) -> Result;
}
impl EventLoopProvider for Nvml {
/**
Create an event loop that will register itself to recieve events for the given
`Device`s.
This function creates an event set and registers each devices' supported event
types for it. The returned `EventLoop` struct then has methods that you can
call to actually utilize it.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `GpuLost`, if any of the given `Device`s have fallen off the bus or are
otherwise inaccessible
* `Unknown`, on any unexpected error
# Platform Support
Only supports Linux.
*/
fn create_event_loop<'nvml>(
&'nvml self,
devices: Vec<&Device<'nvml>>,
) -> Result {
let mut set = self.create_event_set()?;
for d in devices {
set = d.register_events(d.supported_event_types()?, set)?;
}
Ok(EventLoop { set })
}
}
nvml-wrapper-0.10.0/src/high_level/mod.rs 0000644 0000000 0000000 00000000221 00726746425 0016401 0 ustar 0000000 0000000 #[cfg(target_os = "linux")]
pub mod event_loop;
#[cfg(target_os = "linux")]
pub use self::event_loop::{Event, EventLoop, EventLoopProvider};
nvml-wrapper-0.10.0/src/lib.rs 0000644 0000000 0000000 00000116644 00726746425 0014303 0 ustar 0000000 0000000 /*!
A safe and ergonomic Rust wrapper for the [NVIDIA Management Library][nvml] (NVML),
a C-based programmatic interface for monitoring and managing various states within
NVIDIA GPUs.
```
use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn test() -> Result<(), NvmlError> {
let nvml = Nvml::init()?;
// Get the first `Device` (GPU) in the system
let device = nvml.device_by_index(0)?;
let brand = device.brand()?; // GeForce on my system
let fan_speed = device.fan_speed(0)?; // Currently 17% on my system
let power_limit = device.enforced_power_limit()?; // 275k milliwatts on my system
let encoder_util = device.encoder_utilization()?; // Currently 0 on my system; Not encoding anything
let memory_info = device.memory_info()?; // Currently 1.63/6.37 GB used on my system
// ... and there's a whole lot more you can do. Most everything in NVML is wrapped and ready to go
# Ok(())
# }
```
NVML is intended to be a platform for building 3rd-party applications, and is
also the underlying library for NVIDIA's nvidia-smi tool.
## Usage
`nvml-wrapper` builds on top of generated bindings for NVML that make use of the
[`libloading`][libloading] crate. This means the NVML library gets loaded upon
calling `Nvml::init` and can return an error if NVML isn't present, making it
possible to drop NVIDIA-related features in your code at runtime on systems that
don't have relevant hardware.
Successful execution of `Nvml::init` means:
* The NVML library was present on the system and able to be opened
* The function symbol to initialize NVML was loaded and called successfully
* An attempt has been made to load all other NVML function symbols
Every function you call thereafter will individually return an error if it couldn't
be loaded from the NVML library during the `Nvml::init` call.
Note that it's not advised to repeatedly call `Nvml::init` as the constructor
has to perform all the work of loading the function symbols from the library
each time it gets called. Instead, call `Nvml::init` once and store the resulting
`Nvml` instance somewhere to be accessed throughout the lifetime of your program
(perhaps in a [`once_cell`][once_cell]).
## NVML Support
This wrapper is being developed against and currently supports NVML version
11. Each new version of NVML is guaranteed to be backwards-compatible according
to NVIDIA, so this wrapper should continue to work without issue regardless of
NVML version bumps.
### Legacy Functions
Sometimes there will be function-level API version bumps in new NVML releases.
For example:
```text
nvmlDeviceGetComputeRunningProcesses
nvmlDeviceGetComputeRunningProcesses_v2
nvmlDeviceGetComputeRunningProcesses_v3
```
The older versions of the functions will generally continue to work with the
newer NVML releases; however, the newer function versions will not work with
older NVML installs.
By default this wrapper only provides access to the newest function versions.
Enable the `legacy-functions` feature if you require the ability to call older
functions.
## MSRV
The Minimum Supported Rust Version is currently 1.51.0. I will not go out of my
way to avoid bumping this.
## Cargo Features
The `serde` feature can be toggled on in order to `#[derive(Serialize, Deserialize)]`
for every NVML data structure.
[nvml]: https://developer.nvidia.com/nvidia-management-library-nvml
[libloading]: https://github.com/nagisa/rust_libloading
[once_cell]: https://docs.rs/once_cell/latest/once_cell/sync/struct.Lazy.html
*/
#![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 high_level;
pub mod nv_link;
pub mod struct_wrappers;
pub mod structs;
#[cfg(test)]
mod test_utils;
pub mod unit;
// Re-exports for convenience
pub use crate::device::Device;
pub use crate::event::EventSet;
pub use crate::nv_link::NvLink;
pub use crate::unit::Unit;
/// Re-exports from `nvml-wrapper-sys` that are necessary for use of this wrapper.
pub mod sys_exports {
/// Use these constants to populate the `structs::device::FieldId` newtype.
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::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";
/// Determines the major version of the CUDA driver given the full version.
///
/// Obtain the full version via `Nvml.sys_cuda_driver_version()`.
pub fn cuda_driver_version_major(version: i32) -> i32 {
version / 1000
}
/// Determines the minor version of the CUDA driver given the full version.
///
/// Obtain the full version via `NVML.sys_cuda_driver_version()`.
pub fn cuda_driver_version_minor(version: i32) -> i32 {
(version % 1000) / 10
}
/**
The main struct that this library revolves around.
According to NVIDIA's documentation, "It is the user's responsibility to call `nvmlInit()`
before calling any other methods, and `nvmlShutdown()` once NVML is no longer being used."
This struct is used to enforce those rules.
Also according to NVIDIA's documentation, "NVML is thread-safe so it is safe to make
simultaneous NVML calls from multiple threads." In the Rust world, this translates to `NVML`
being `Send` + `Sync`. You can `.clone()` an `Arc` wrapped `NVML` and enjoy using it on any thread.
NOTE: If you care about possible errors returned from `nvmlShutdown()`, use the `.shutdown()`
method on this struct. **The `Drop` implementation ignores errors.**
When reading documentation on this struct and its members, remember that a lot of it,
especially in regards to errors returned, is copied from NVIDIA's docs. While they can be found
online [here](http://docs.nvidia.com/deploy/nvml-api/index.html), the hosted docs sometimes outdated
and may not accurately reflect the version of NVML that this library is written for; beware. You
should ideally read the doc comments on an up-to-date NVML API header. Such a header can be
downloaded as part of the [CUDA toolkit](https://developer.nvidia.com/cuda-downloads).
*/
pub struct Nvml {
lib: ManuallyDrop,
}
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")
}
}
impl Nvml {
/**
Handles NVML initialization and must be called before doing anything else.
While it is possible to initialize `NVML` multiple times (NVIDIA's docs state
that reference counting is used internally), you should strive to initialize
`NVML` once at the start of your program's execution; the constructors handle
dynamically loading function symbols from the `NVML` lib and are therefore
somewhat expensive.
Note that this will initialize NVML but not any GPUs. This means that NVML can
communicate with a GPU even when other GPUs in a system are bad or unstable.
By default, initialization looks for "libnvidia-ml.so" on linux and "nvml.dll"
on Windows. These default names should work for default installs on those
platforms; if further specification is required, use `Nvml::builder`.
# Errors
* `DriverNotLoaded`, if the NVIDIA driver is not running
* `NoPermission`, if NVML does not have permission to talk to the driver
* `Unknown`, on any unexpected error
*/
// Checked against local
#[doc(alias = "nvmlInit_v2")]
pub fn init() -> Result {
Self::init_internal(LIB_PATH)
}
fn init_internal(path: impl AsRef) -> Result {
let lib = unsafe {
let lib = NvmlLib::new(path)?;
let sym = nvml_sym(lib.nvmlInit_v2.as_ref())?;
nvml_try(sym())?;
ManuallyDrop::new(lib)
};
Ok(Self { lib })
}
/**
An initialization function that allows you to pass flags to control certain behaviors.
This is the same as `init()` except for the addition of flags.
# Errors
* `DriverNotLoaded`, if the NVIDIA driver is not running
* `NoPermission`, if NVML does not have permission to talk to the driver
* `Unknown`, on any unexpected error
# Examples
```
# use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
use nvml_wrapper::bitmasks::InitFlags;
# fn main() -> Result<(), NvmlError> {
// Don't fail if the system doesn't have any NVIDIA GPUs
//
// Also, don't attach any GPUs during initialization
Nvml::init_with_flags(InitFlags::NO_GPUS | InitFlags::NO_ATTACH)?;
# Ok(())
# }
```
*/
#[doc(alias = "nvmlInitWithFlags")]
pub fn init_with_flags(flags: InitFlags) -> Result {
Self::init_with_flags_internal(LIB_PATH, flags)
}
fn init_with_flags_internal(
path: impl AsRef,
flags: InitFlags,
) -> Result {
let lib = unsafe {
let lib = NvmlLib::new(path)?;
let sym = nvml_sym(lib.nvmlInitWithFlags.as_ref())?;
nvml_try(sym(flags.bits()))?;
ManuallyDrop::new(lib)
};
Ok(Self { lib })
}
/// Create an `NvmlBuilder` for further flexibility in how NVML is initialized.
pub fn builder<'a>() -> NvmlBuilder<'a> {
NvmlBuilder::default()
}
/**
Use this to shutdown NVML and release allocated resources if you care about handling
potential errors (*the `Drop` implementation ignores errors!*).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `Unknown`, on any unexpected error
*/
// Thanks to `sorear` on IRC for suggesting this approach
// Checked against local
// Tested
#[doc(alias = "nvmlShutdown")]
pub fn shutdown(mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(self.lib.nvmlShutdown.as_ref())?;
unsafe {
nvml_try(sym())?;
}
// SAFETY: we `mem::forget(self)` after this, so `self.lib` won't get
// touched by our `Drop` impl
let lib = unsafe { ManuallyDrop::take(&mut self.lib) };
mem::forget(self);
Ok(lib.__library.close()?)
}
/**
Get the number of compute devices in the system (compute device == one GPU).
Note that this count can include devices you do not have permission to access.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetCount_v2")]
pub fn device_count(&self) -> Result {
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)
}
}
/**
Gets the version of the system's graphics driver and returns it as an alphanumeric
string.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
*/
// Checked against local
// Tested
#[doc(alias = "nvmlSystemGetDriverVersion")]
pub fn sys_driver_version(&self) -> Result {
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())
}
}
/**
Gets the version of the system's NVML library and returns it as an alphanumeric
string.
# Errors
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
*/
// Checked against local
// Tested
#[doc(alias = "nvmlSystemGetNVMLVersion")]
pub fn sys_nvml_version(&self) -> Result {
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,
))?;
// Thanks to `Amaranth` on IRC for help with this
let version_raw = CStr::from_ptr(version_vec.as_ptr());
Ok(version_raw.to_str()?.into())
}
}
/**
Gets the version of the system's CUDA driver.
Calls into the CUDA library (cuDriverGetVersion()).
You can use `cuda_driver_version_major` and `cuda_driver_version_minor`
to get the major and minor driver versions from this number.
# Errors
* `FunctionNotFound`, if cuDriverGetVersion() is not found in the shared library
* `LibraryNotFound`, if libcuda.so.1 or libcuda.dll cannot be found
*/
#[doc(alias = "nvmlSystemGetCudaDriverVersion_v2")]
pub fn sys_cuda_driver_version(&self) -> Result {
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)
}
}
/**
Gets the name of the process for the given process ID, cropped to the provided length.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the length is 0 (if this is returned without length being 0, file an issue)
* `NotFound`, if the process does not exist
* `NoPermission`, if the user doesn't have permission to perform the operation
* `Utf8Error`, if the string obtained from the C function is not valid UTF-8. NVIDIA's docs say
that the string encoding is ANSI, so this may very well happen.
* `Unknown`, on any unexpected error
*/
// TODO: The docs say the string is ANSI-encoded. Not sure if I should try
// to do anything about that
// Checked against local
// Tested
#[doc(alias = "nvmlSystemGetProcessName")]
pub fn sys_process_name(&self, pid: u32, length: usize) -> Result {
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())
}
}
/**
Acquire the handle for a particular device based on its index (starts at 0).
Usage of this function causes NVML to initialize the target GPU. Additional
GPUs may be initialized if the target GPU is an SLI slave.
You can determine valid indices by using `.device_count()`. This
function doesn't call that for you, but the actual C function to get
the device handle will return an error in the case of an invalid index.
This means that the `InvalidArg` error will be returned if you pass in
an invalid index.
NVIDIA's docs state that "The order in which NVML enumerates devices has
no guarantees of consistency between reboots. For that reason it is recommended
that devices be looked up by their PCI ids or UUID." In this library, that translates
into usage of `.device_by_uuid()` and `.device_by_pci_bus_id()`.
The NVML index may not correlate with other APIs such as the CUDA device index.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if index is invalid
* `InsufficientPower`, if any attached devices have improperly attached external power cables
* `NoPermission`, if the user doesn't have permission to talk to this device
* `IrqIssue`, if the NVIDIA kernel detected an interrupt issue with the attached GPUs
* `GpuLost`, if the target GPU has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetHandleByIndex_v2")]
pub fn device_by_index(&self, index: u32) -> Result {
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))
}
}
/**
Acquire the handle for a particular device based on its PCI bus ID.
Usage of this function causes NVML to initialize the target GPU. Additional
GPUs may be initialized if the target GPU is an SLI slave.
The bus ID corresponds to the `bus_id` returned by `Device.pci_info()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if `pci_bus_id` is invalid
* `NotFound`, if `pci_bus_id` does not match a valid device on the system
* `InsufficientPower`, if any attached devices have improperly attached external power cables
* `NoPermission`, if the user doesn't have permission to talk to this device
* `IrqIssue`, if the NVIDIA kernel detected an interrupt issue with the attached GPUs
* `GpuLost`, if the target GPU has fallen off the bus or is otherwise inaccessible
* `NulError`, for which you can read the docs on `std::ffi::NulError`
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetHandleByPciBusId_v2")]
pub fn device_by_pci_bus_id>(&self, pci_bus_id: S) -> Result
where
Vec: From,
{
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))
}
}
/// Not documenting this because it's deprecated and does not seem to work
/// anymore.
// Tested (for an error)
#[deprecated(note = "use `.device_by_uuid()`, this errors on dual GPU boards")]
#[doc(alias = "nvmlDeviceGetHandleBySerial")]
pub fn device_by_serial>(&self, board_serial: S) -> Result
where
Vec: From,
{
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))
}
}
/**
Acquire the handle for a particular device based on its globally unique immutable
UUID.
Usage of this function causes NVML to initialize the target GPU. Additional
GPUs may be initialized as the function called within searches for the target GPU.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if `uuid` is invalid
* `NotFound`, if `uuid` does not match a valid device on the system
* `InsufficientPower`, if any attached devices have improperly attached external power cables
* `IrqIssue`, if the NVIDIA kernel detected an interrupt issue with the attached GPUs
* `GpuLost`, if the target GPU has fallen off the bus or is otherwise inaccessible
* `NulError`, for which you can read the docs on `std::ffi::NulError`
* `Unknown`, on any unexpected error
NVIDIA doesn't mention `NoPermission` for this one. Strange!
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceGetHandleByUUID")]
pub fn device_by_uuid>(&self, uuid: S) -> Result
where
Vec: From,
{
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))
}
}
/**
Gets the common ancestor for two devices.
Note: this is the same as `Device.topology_common_ancestor()`.
# Errors
* `InvalidArg`, if the device is invalid
* `NotSupported`, if this `Device` or the OS does not support this feature
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Platform Support
Only supports Linux.
*/
// Checked against local
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetTopologyCommonAncestor")]
pub fn topology_common_ancestor(
&self,
device1: &Device,
device2: &Device,
) -> Result {
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)
}
}
/**
Acquire the handle for a particular `Unit` based on its index.
Valid indices are derived from the count returned by `.unit_count()`.
For example, if `unit_count` is 2 the valid indices are 0 and 1, corresponding
to UNIT 0 and UNIT 1.
Note that the order in which NVML enumerates units has no guarantees of
consistency between reboots.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if `index` is invalid
* `Unknown`, on any unexpected error
# Device Support
For S-class products.
*/
// Checked against local
// Tested (for an error)
#[doc(alias = "nvmlUnitGetHandleByIndex")]
pub fn unit_by_index(&self, index: u32) -> Result {
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))
}
}
/**
Checks if the passed-in `Device`s are on the same physical board.
Note: this is the same as `Device.is_on_same_board_as()`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if either `Device` is invalid
* `NotSupported`, if this check is not supported by this `Device`
* `GpuLost`, if this `Device` has fallen off the bus or is otherwise inaccessible
* `Unknown`, on any unexpected error
*/
// Checked against local
// Tested
#[doc(alias = "nvmlDeviceOnSameBoard")]
pub fn are_devices_on_same_board(
&self,
device1: &Device,
device2: &Device,
) -> Result {
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),
}
}
}
/**
Gets the set of GPUs that have a CPU affinity with the given CPU number.
# Errors
* `InvalidArg`, if `cpu_number` is invalid
* `NotSupported`, if this `Device` or the OS does not support this feature
* `Unknown`, an error has occurred in the underlying topology discovery
# Platform Support
Only supports Linux.
*/
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlSystemGetTopologyGpuSet")]
pub fn topology_gpu_set(&self, cpu_number: u32) -> Result, 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 = 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())
}
}
// Helper function for the above.
#[cfg(target_os = "linux")]
fn topology_gpu_set_count(&self, cpu_number: u32) -> Result {
let sym = nvml_sym(self.lib.nvmlSystemGetTopologyGpuSet.as_ref())?;
unsafe {
// Indicates that we want the count
let mut count: c_uint = 0;
// Passing null doesn't indicate that we want the count, just allowed
nvml_try(sym(cpu_number, &mut count, ptr::null_mut()))?;
Ok(count)
}
}
/**
Gets the IDs and firmware versions for any Host Interface Cards in the system.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
# Device Support
Supports S-class products.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlSystemGetHicVersion")]
pub fn hic_versions(&self) -> Result, 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 = vec![mem::zeroed(); count as usize];
nvml_try(sym(&mut count, hics.as_mut_ptr()))?;
hics.into_iter().map(HwbcEntry::try_from).collect()
}
}
/**
Gets the count of Host Interface Cards in the system.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
# Device Support
Supports S-class products.
*/
// Tested as part of the above method
#[doc(alias = "nvmlSystemGetHicVersion")]
pub fn hic_count(&self) -> Result {
let sym = nvml_sym(self.lib.nvmlSystemGetHicVersion.as_ref())?;
unsafe {
/*
NVIDIA doesn't even say that `count` will be set to the count if
`InsufficientSize` is returned. But we can assume sanity, right?
The idea here is:
If there are 0 HICs, NVML_SUCCESS is returned, `count` is set
to 0. We return count, all good.
If there is 1 HIC, NVML_SUCCESS is returned, `count` is set to
1. We return count, all good.
If there are >= 2 HICs, NVML_INSUFFICIENT_SIZE is returned.
`count` is theoretically set to the actual count, and we
return it.
*/
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)
}
// We know that this will be an error
other => nvml_try(other).map(|_| 0),
}
}
}
/**
Gets the number of units in the system.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `Unknown`, on any unexpected error
# Device Support
Supports S-class products.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlUnitGetCount")]
pub fn unit_count(&self) -> Result {
let sym = nvml_sym(self.lib.nvmlUnitGetCount.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(&mut count))?;
Ok(count)
}
}
/**
Create an empty set of events.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `Unknown`, on any unexpected error
# Device Support
Supports Fermi and newer fully supported devices.
*/
// Checked against local
// Tested
#[doc(alias = "nvmlEventSetCreate")]
pub fn create_event_set(&self) -> Result {
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))
}
}
/**
Request the OS and the NVIDIA kernel driver to rediscover a portion of the PCI
subsystem in search of GPUs that were previously removed.
The portion of the PCI tree can be narrowed by specifying a domain, bus, and
device in the passed-in `pci_info`. **If all of these fields are zeroes, the
entire PCI tree will be searched.** Note that for long-running NVML processes,
the enumeration of devices will change based on how many GPUs are discovered
and where they are inserted in bus order.
All newly discovered GPUs will be initialized and have their ECC scrubbed which
may take several seconds per GPU. **All device handles are no longer guaranteed
to be valid post discovery**. I am not sure if this means **all** device
handles, literally, or if NVIDIA is referring to handles that had previously
been obtained to devices that were then removed and have now been
re-discovered.
Must be run as administrator.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `OperatingSystem`, if the operating system is denying this feature
* `NoPermission`, if the calling process has insufficient permissions to
perform this operation
* `NulError`, if an issue is encountered when trying to convert a Rust
`String` into a `CString`.
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal and newer fully supported devices.
Some Kepler devices are also supported (that's all NVIDIA says, no specifics).
# Platform Support
Only supports Linux.
*/
// TODO: constructor for default pci_infos ^
// Checked against local
// Tested
#[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()?)) }
}
/**
Gets the number of excluded GPU devices in the system.
# Device Support
Supports all devices.
*/
#[doc(alias = "nvmlGetExcludedDeviceCount")]
pub fn excluded_device_count(&self) -> Result {
let sym = nvml_sym(self.lib.nvmlGetExcludedDeviceCount.as_ref())?;
unsafe {
let mut count: c_uint = mem::zeroed();
nvml_try(sym(&mut count))?;
Ok(count)
}
}
/**
Gets information for the specified excluded device.
# Errors
* `InvalidArg`, if the given index is invalid
* `Utf8Error`, if strings obtained from the C function are not valid Utf8
# Device Support
Supports all devices.
*/
#[doc(alias = "nvmlGetExcludedDeviceInfoByIndex")]
pub fn excluded_device_info(&self, index: u32) -> Result {
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)
}
}
}
/// This `Drop` implementation ignores errors! Use the `.shutdown()` method on
/// the `Nvml` struct
/// if you care about handling them.
impl Drop for Nvml {
#[doc(alias = "nvmlShutdown")]
fn drop(&mut self) {
unsafe {
self.lib.nvmlShutdown();
// SAFETY: called after the last usage of `self.lib`
ManuallyDrop::drop(&mut self.lib);
}
}
}
/**
A builder struct that provides further flexibility in how NVML is initialized.
# Examples
Initialize NVML with a non-default name for the shared object file:
```
use nvml_wrapper::Nvml;
use std::ffi::OsStr;
let init_result = Nvml::builder().lib_path(OsStr::new("libnvidia-ml-other-name.so")).init();
```
Initialize NVML with a non-default path to the shared object file:
```
use nvml_wrapper::Nvml;
use std::ffi::OsStr;
let init_result = Nvml::builder().lib_path(OsStr::new("/some/path/to/libnvidia-ml.so")).init();
```
*/
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct NvmlBuilder<'a> {
lib_path: Option<&'a OsStr>,
flags: InitFlags,
}
impl<'a> NvmlBuilder<'a> {
/**
Set the path to the NVML lib file.
See [`libloading`'s docs][libloading] for details about how this lib path is
handled.
[libloading]: https://docs.rs/libloading/0.6.6/libloading/struct.Library.html#method.new
*/
pub fn lib_path(&mut self, path: &'a OsStr) -> &mut Self {
self.lib_path = Some(path);
self
}
/// Set the `InitFlags` to initialize NVML with.
pub fn flags(&mut self, flags: InitFlags) -> &mut Self {
self.flags = flags;
self
}
/// Perform initialization.
pub fn init(&self) -> Result {
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)
})
}
// Can't get serial on my machine
#[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)
})
}
// I don't have 2 devices
#[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");
}
// Errors on my machine
#[test]
#[ignore = "my machine does not support this call"]
fn unit_by_index() {
let nvml = nvml();
test(3, || nvml.unit_by_index(0))
}
// I don't have 2 devices
#[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()?;
// We don't test with admin perms and therefore expect an error
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))
}
}
}
nvml-wrapper-0.10.0/src/nv_link.rs 0000644 0000000 0000000 00000047207 00726746425 0015173 0 ustar 0000000 0000000 use crate::Device;
use crate::enum_wrappers::{
bool_from_state,
nv_link::{Capability, ErrorCounter},
state_from_bool,
};
use crate::enums::nv_link::Counter;
use crate::error::{nvml_sym, nvml_try, NvmlError};
use crate::ffi::bindings::*;
use crate::struct_wrappers::{device::PciInfo, nv_link::UtilizationControl};
use crate::structs::nv_link::UtilizationCounter;
use std::{
convert::TryFrom,
mem,
os::raw::{c_uint, c_ulonglong},
};
use static_assertions::assert_impl_all;
/**
Struct that represents a `Device`'s NvLink.
Obtain this via `Device.link_wrapper_for()`.
Lifetimes are used to enforce that each `NvLink` instance cannot be used after
the `Device` instance it was obtained from is dropped:
```compile_fail
use nvml_wrapper::Nvml;
# use nvml_wrapper::error::*;
# fn main() -> Result<(), NvmlError> {
let nvml = Nvml::init()?;
let device = nvml.device_by_index(0)?;
let link = device.link_wrapper_for(0);
drop(device);
// This won't compile
link.is_active()?;
# Ok(())
# }
```
Note that I cannot test any `NvLink` methods myself as I do not have access to
such a link setup. **Test the functionality in this module before you use it**.
*/
#[derive(Debug)]
pub struct NvLink<'device, 'nvml: 'device> {
pub(crate) device: &'device Device<'nvml>,
pub(crate) link: c_uint,
}
assert_impl_all!(NvLink: Send, Sync);
impl<'device, 'nvml: 'device> NvLink<'device, 'nvml> {
/// Obtain the `Device` reference stored within this struct.
pub fn device(&self) -> &Device {
self.device
}
/// Obtain the value of this struct's `link` field.
pub fn link(&self) -> u32 {
self.link
}
/**
Gets whether or not this `Device`'s NvLink is active.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `UnexpectedVariant`, for which you can read the docs for
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// Test written
#[doc(alias = "nvmlDeviceGetNvLinkState")]
pub fn is_active(&self) -> Result {
let sym = nvml_sym(self.device.nvml().lib.nvmlDeviceGetNvLinkState.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device.handle(), self.link, &mut state))?;
bool_from_state(state)
}
}
/**
Gets the NvLink version of this `Device` / `NvLink`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// Test written
#[doc(alias = "nvmlDeviceGetNvLinkVersion")]
pub fn version(&self) -> Result {
let sym = nvml_sym(self.device.nvml().lib.nvmlDeviceGetNvLinkVersion.as_ref())?;
unsafe {
let mut version: c_uint = mem::zeroed();
nvml_try(sym(self.device.handle(), self.link, &mut version))?;
Ok(version)
}
}
/**
Gets whether or not this `Device` / `NvLink` has a `Capability`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// Test written
#[doc(alias = "nvmlDeviceGetNvLinkCapability")]
pub fn has_capability(&self, cap_type: Capability) -> Result {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkCapability
.as_ref(),
)?;
unsafe {
// NVIDIA says that this should be interpreted as a boolean
let mut capability: c_uint = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
cap_type.as_c(),
&mut capability,
))?;
#[allow(clippy::match_like_matches_macro)]
Ok(match capability {
0 => false,
// Not worth an error or a panic if the value is > 1
_ => true,
})
}
}
/**
Gets the PCI information for this `NvLink`'s remote node.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// Test written
#[doc(alias = "nvmlDeviceGetNvLinkRemotePciInfo_v2")]
pub fn remote_pci_info(&self) -> Result {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkRemotePciInfo_v2
.as_ref(),
)?;
unsafe {
let mut pci_info: nvmlPciInfo_t = mem::zeroed();
nvml_try(sym(self.device.handle(), self.link, &mut pci_info))?;
PciInfo::try_from(pci_info, false)
}
}
/**
Gets the specified `ErrorCounter` value.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// Test written
#[doc(alias = "nvmlDeviceGetNvLinkErrorCounter")]
pub fn error_counter(&self, counter: ErrorCounter) -> Result {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkErrorCounter
.as_ref(),
)?;
unsafe {
let mut value: c_ulonglong = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
counter.as_c(),
&mut value,
))?;
Ok(value)
}
}
/**
Resets all error counters to zero.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// No-run test written
#[doc(alias = "nvmlDeviceResetNvLinkErrorCounters")]
pub fn reset_error_counters(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceResetNvLinkErrorCounters
.as_ref(),
)?;
unsafe { nvml_try(sym(self.device.handle(), self.link)) }
}
/**
Sets the NvLink utilization counter control information for the specified
`Counter`.
The counters will be reset if `reset_counters` is true.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// No-run test written
#[doc(alias = "nvmlDeviceSetNvLinkUtilizationControl")]
pub fn set_utilization_control(
&mut self,
counter: Counter,
settings: UtilizationControl,
reset_counters: bool,
) -> Result<(), NvmlError> {
let reset: c_uint = u32::from(reset_counters);
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceSetNvLinkUtilizationControl
.as_ref(),
)?;
unsafe {
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
&mut settings.as_c(),
reset,
))
}
}
/**
Gets the NvLink utilization counter control information for the specified
`Counter`.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// Test written
#[doc(alias = "nvmlDeviceGetNvLinkUtilizationControl")]
pub fn utilization_control(&self, counter: Counter) -> Result {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkUtilizationControl
.as_ref(),
)?;
unsafe {
let mut controls: nvmlNvLinkUtilizationControl_t = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
&mut controls,
))?;
UtilizationControl::try_from(controls)
}
}
/**
Gets the NvLink utilization counter for the given `counter`.
The retrieved values are based on the current controls set for the specified
`Counter`. **You should use `.set_utilization_control()` before calling this**
as the utilization counters have no default state.
I do not attempt to verify, statically or at runtime, that you have controls
set for `counter` prior to calling this method on `counter`. NVIDIA says that
it is "In general\[,\] good practice", which does not sound to me as if it
is in any way unsafe to make this call without having set controls. I don't
believe it's worth the overhead of using a `Mutex`'d bool to track whether
or not you have set controls, and it's certainly not worth the effort to
statically verify it via the type system.
That being said, I don't know what exactly would happen, either, and I have
no means of finding out. If you do and discover that garbage values are
returned, for instance, I would love to hear about it; that would likely
cause this decision to be reconsidered.
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// No-run test written
#[doc(alias = "nvmlDeviceGetNvLinkUtilizationCounter")]
pub fn utilization_counter(&self, counter: Counter) -> Result {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkUtilizationCounter
.as_ref(),
)?;
unsafe {
let mut receive: c_ulonglong = mem::zeroed();
let mut send: c_ulonglong = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
&mut receive,
&mut send,
))?;
Ok(UtilizationCounter { receive, send })
}
}
/**
Freezes the specified NvLink utilization `Counter`.
Both the receive and send counters will be frozen (if I'm reading NVIDIA's
meaning correctly).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// No-run test written
#[doc(alias = "nvmlDeviceFreezeNvLinkUtilizationCounter")]
pub fn freeze_utilization_counter(&mut self, counter: Counter) -> Result<(), NvmlError> {
self.set_utilization_counter_frozen(counter, true)
}
/**
Unfreezes the specified NvLink utilization `Counter`.
Both the receive and send counters will be unfrozen (if I'm reading NVIDIA's
meaning correctly).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// No-run test written
#[doc(alias = "nvmlDeviceFreezeNvLinkUtilizationCounter")]
pub fn unfreeze_utilization_counter(&mut self, counter: Counter) -> Result<(), NvmlError> {
self.set_utilization_counter_frozen(counter, false)
}
fn set_utilization_counter_frozen(
&mut self,
counter: Counter,
frozen: bool,
) -> Result<(), NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceFreezeNvLinkUtilizationCounter
.as_ref(),
)?;
unsafe {
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
state_from_bool(frozen),
))
}
}
/**
Resets the specified NvLink utilization `Counter`.
Both the receive and send counters will be rest (if I'm reading NVIDIA's
meaning correctly).
# Errors
* `Uninitialized`, if the library has not been successfully initialized
* `InvalidArg`, if the `link` or `Device` within this `NvLink` struct instance
is invalid
* `NotSupported`, if this `Device` doesn't support this feature
* `Unknown`, on any unexpected error
# Device Support
Supports Pascal or newer fully supported devices.
*/
// No-run test written
#[doc(alias = "nvmlDeviceResetNvLinkUtilizationCounter")]
pub fn reset_utilization_counter(&mut self, counter: Counter) -> Result<(), NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceResetNvLinkUtilizationCounter
.as_ref(),
)?;
unsafe { nvml_try(sym(self.device.handle(), self.link, counter as c_uint)) }
}
}
#[cfg(test)]
#[deny(unused_mut)]
mod test {
use crate::bitmasks::nv_link::*;
use crate::enum_wrappers::nv_link::*;
use crate::enums::nv_link::*;
use crate::struct_wrappers::nv_link::*;
use crate::test_utils::*;
#[test]
#[ignore = "my machine does not support this call"]
fn is_active() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.is_active())
}
#[test]
#[ignore = "my machine does not support this call"]
fn version() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.version())
}
#[test]
#[ignore = "my machine does not support this call"]
fn has_capability() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.has_capability(Capability::P2p))
}
#[test]
#[ignore = "my machine does not support this call"]
fn remote_pci_info() {
let nvml = nvml();
test_with_link(3, &nvml, |link| {
let info = link.remote_pci_info()?;
assert_eq!(info.pci_sub_system_id, None);
Ok(info)
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn error_counter() {
let nvml = nvml();
test_with_link(3, &nvml, |link| {
link.error_counter(ErrorCounter::DlRecovery)
})
}
// This modifies link state, so we don't want to actually run the test
#[allow(dead_code)]
fn reset_error_counters() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.reset_error_counters().unwrap();
}
// This modifies link state, so we don't want to actually run the test
#[allow(dead_code)]
fn set_utilization_control() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
let settings = UtilizationControl {
units: UtilizationCountUnit::Cycles,
packet_filter: PacketTypes::NO_OP
| PacketTypes::READ
| PacketTypes::WRITE
| PacketTypes::RATOM
| PacketTypes::WITH_DATA,
};
link.set_utilization_control(Counter::One, settings, false)
.unwrap()
}
#[test]
#[ignore = "my machine does not support this call"]
fn utilization_control() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.utilization_control(Counter::One))
}
// This shouldn't be called without modifying link state, so we don't want
// to actually run the test
#[allow(dead_code)]
fn utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let link = device.link_wrapper_for(0);
link.utilization_counter(Counter::One).unwrap();
}
// This modifies link state, so we don't want to actually run the test
#[allow(dead_code)]
fn freeze_utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.freeze_utilization_counter(Counter::One).unwrap();
}
// This modifies link state, so we don't want to actually run the test
#[allow(dead_code)]
fn unfreeze_utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.unfreeze_utilization_counter(Counter::One).unwrap();
}
// This modifies link state, so we don't want to actually run the test
#[allow(dead_code)]
fn reset_utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.reset_utilization_counter(Counter::One).unwrap();
}
}
nvml-wrapper-0.10.0/src/struct_wrappers/device.rs 0000644 0000000 0000000 00000055732 00726746425 0020243 0 ustar 0000000 0000000 use crate::bitmasks::device::FbcFlags;
use crate::enum_wrappers::device::{BridgeChip, EncoderType, FbcSessionType, SampleValueType};
use crate::enums::device::{FirmwareVersion, SampleValue, UsedGpuMemory};
use crate::error::{nvml_try, Bits, NvmlError};
use crate::ffi::bindings::*;
use crate::structs::device::FieldId;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use std::{
cmp::Ordering,
ffi::{CStr, CString},
};
use std::{
convert::{TryFrom, TryInto},
os::raw::c_char,
};
/// PCI information about a GPU device.
// Checked against local
// Tested
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PciInfo {
/// The bus on which the device resides, 0 to 0xff.
pub bus: u32,
/// The PCI identifier.
pub bus_id: String,
/// The device's ID on the bus, 0 to 31.
pub device: u32,
/// The PCI domain on which the device's bus resides, 0 to 0xffff.
pub domain: u32,
/// The combined 16-bit device ID and 16-bit vendor ID.
pub pci_device_id: u32,
/**
The 32-bit Sub System Device ID.
Will always be `None` if this `PciInfo` was obtained from `NvLink.remote_pci_info()`.
NVIDIA says that the C field that this corresponds to "is not filled ... and
is indeterminate" when being returned from that specific call.
Will be `Some` in all other cases.
*/
pub pci_sub_system_id: Option,
}
impl PciInfo {
/**
Try to create this struct from its C equivalent.
Passing `false` for `sub_sys_id_present` will set the `pci_sub_system_id`
field to `None`. See the field docs for more.
# Errors
* `Utf8Error`, if the string obtained from the C function is not valid Utf8
*/
pub fn try_from(struct_: nvmlPciInfo_t, sub_sys_id_present: bool) -> Result {
unsafe {
let bus_id_raw = CStr::from_ptr(struct_.busId.as_ptr());
Ok(Self {
bus: struct_.bus,
bus_id: bus_id_raw.to_str()?.into(),
device: struct_.device,
domain: struct_.domain,
pci_device_id: struct_.pciDeviceId,
pci_sub_system_id: if sub_sys_id_present {
Some(struct_.pciSubSystemId)
} else {
None
},
})
}
}
}
impl TryInto for PciInfo {
type Error = NvmlError;
/**
Convert this `PciInfo` back into its C equivalent.
# Errors
* `NulError`, if a nul byte was found in the bus_id (shouldn't occur?)
* `StringTooLong`, if `bus_id.len()` exceeded the length of
`NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE`. This should (?) only be able to
occur if the user modifies `bus_id` in some fashion. We return an error
rather than panicking.
*/
fn try_into(self) -> Result {
// This is more readable than spraying `buf_size as usize` everywhere
const fn buf_size() -> usize {
NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE as usize
}
let mut bus_id_c: [c_char; buf_size()] = [0; buf_size()];
let mut bus_id = CString::new(self.bus_id)?.into_bytes_with_nul();
// Make the string the same length as the array we need to clone it to
match bus_id.len().cmp(&buf_size()) {
Ordering::Less => {
while bus_id.len() != buf_size() {
bus_id.push(0);
}
}
Ordering::Equal => {
// No need to do anything; the buffers are already the same length
}
Ordering::Greater => {
return Err(NvmlError::StringTooLong {
max_len: buf_size(),
actual_len: bus_id.len(),
})
}
}
bus_id_c.clone_from_slice(&bus_id.into_iter().map(|b| b as c_char).collect::>());
Ok(nvmlPciInfo_t {
busIdLegacy: [0; NVML_DEVICE_PCI_BUS_ID_BUFFER_V2_SIZE as usize],
domain: self.domain,
bus: self.bus,
device: self.device,
pciDeviceId: self.pci_device_id,
// This seems the most correct thing to do? Since this should only
// be none if obtained from `NvLink.remote_pci_info()`.
pciSubSystemId: self.pci_sub_system_id.unwrap_or(0),
busId: bus_id_c,
})
}
}
/// BAR1 memory allocation information for a device (in bytes)
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BAR1MemoryInfo {
/// Unallocated
pub free: u64,
/// Total memory
pub total: u64,
/// Allocated
pub used: u64,
}
impl From for BAR1MemoryInfo {
fn from(struct_: nvmlBAR1Memory_t) -> Self {
Self {
free: struct_.bar1Free,
total: struct_.bar1Total,
used: struct_.bar1Used,
}
}
}
/// Information about a bridge chip.
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BridgeChipInfo {
pub fw_version: FirmwareVersion,
pub chip_type: BridgeChip,
}
impl TryFrom for BridgeChipInfo {
type Error = NvmlError;
/**
Construct `BridgeChipInfo` from the corresponding C struct.
# Errors
* `UnexpectedVariant`, for which you can read the docs for
*/
fn try_from(value: nvmlBridgeChipInfo_t) -> Result {
let fw_version = FirmwareVersion::from(value.fwVersion);
let chip_type = BridgeChip::try_from(value.type_)?;
Ok(Self {
fw_version,
chip_type,
})
}
}
/**
This struct stores the complete hierarchy of the bridge chip within the board.
The immediate bridge is stored at index 0 of `chips_hierarchy`. The parent to
the immediate bridge is at index 1, and so forth.
*/
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BridgeChipHierarchy {
/// Hierarchy of bridge chips on the board.
pub chips_hierarchy: Vec,
/// Number of bridge chips on the board.
pub chip_count: u8,
}
impl TryFrom for BridgeChipHierarchy {
type Error = NvmlError;
/**
Construct `BridgeChipHierarchy` from the corresponding C struct.
# Errors
* `UnexpectedVariant`, for which you can read the docs for
*/
fn try_from(value: nvmlBridgeChipHierarchy_t) -> Result {
let chips_hierarchy = value
.bridgeChipInfo
.iter()
.map(|bci| BridgeChipInfo::try_from(*bci))
.collect::>()?;
Ok(Self {
chips_hierarchy,
chip_count: value.bridgeCount,
})
}
}
/// Information about compute processes running on the GPU.
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ProcessInfo {
// Process ID.
pub pid: u32,
/// Amount of used GPU memory in bytes.
pub used_gpu_memory: UsedGpuMemory,
/// The ID of the GPU instance this process is running on, if applicable.
///
/// MIG (Multi-Instance GPU) must be enabled on the device for this field
/// to be set.
pub gpu_instance_id: Option,
/// The ID of the compute instance this process is running on, if applicable.
///
/// MIG (Multi-Instance GPU) must be enabled on the device for this field
/// to be set.
pub compute_instance_id: Option,
}
impl From for ProcessInfo {
fn from(struct_: nvmlProcessInfo_t) -> Self {
const NO_VALUE: u32 = 0xFFFFFFFF;
let gpu_instance_id = Some(struct_.gpuInstanceId).filter(|id| *id != NO_VALUE);
let compute_instance_id = Some(struct_.computeInstanceId).filter(|id| *id != NO_VALUE);
Self {
pid: struct_.pid,
used_gpu_memory: UsedGpuMemory::from(struct_.usedGpuMemory),
gpu_instance_id,
compute_instance_id,
}
}
}
/// Detailed ECC error counts for a device.
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EccErrorCounts {
pub device_memory: u64,
pub l1_cache: u64,
pub l2_cache: u64,
pub register_file: u64,
}
impl From for EccErrorCounts {
fn from(struct_: nvmlEccErrorCounts_t) -> Self {
Self {
device_memory: struct_.deviceMemory,
l1_cache: struct_.l1Cache,
l2_cache: struct_.l2Cache,
register_file: struct_.registerFile,
}
}
}
/// Memory allocation information for a device (in bytes).
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MemoryInfo {
/// Unallocated FB memory.
pub free: u64,
/// Total installed FB memory.
pub total: u64,
/// Allocated FB memory.
///
/// Note that the driver/GPU always sets aside a small amount of memory for
/// bookkeeping.
pub used: u64,
}
impl From for MemoryInfo {
fn from(struct_: nvmlMemory_t) -> Self {
Self {
free: struct_.free,
total: struct_.total,
used: struct_.used,
}
}
}
/// Utilization information for a device. Each sample period may be between 1
/// second and 1/6 second, depending on the product being queried.
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Utilization {
/// Percent of time over the past sample period during which one or more
/// kernels was executing on the GPU.
pub gpu: u32,
/// Percent of time over the past sample period during which global (device)
/// memory was being read or written to.
pub memory: u32,
}
impl From for Utilization {
fn from(struct_: nvmlUtilization_t) -> Self {
Self {
gpu: struct_.gpu,
memory: struct_.memory,
}
}
}
/// Performance policy violation status data.
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ViolationTime {
/// Represents CPU timestamp in microseconds.
pub reference_time: u64,
/// Violation time in nanoseconds.
pub violation_time: u64,
}
impl From for ViolationTime {
fn from(struct_: nvmlViolationTime_t) -> Self {
Self {
reference_time: struct_.referenceTime,
violation_time: struct_.violationTime,
}
}
}
/**
Accounting statistics for a process.
There is a field: `unsigned int reserved[5]` present on the C struct that this wraps
that NVIDIA says is "reserved for future use." If it ever gets used in the future,
an equivalent wrapping field will have to be added to this struct.
*/
// Checked against local
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AccountingStats {
/**
Percent of time over the process's lifetime during which one or more kernels was
executing on the GPU. This is just like what is returned by
`Device.utilization_rates()` except it is for the lifetime of a process (not just
the last sample period).
It will be `None` if `Device.utilization_rates()` is not supported.
*/
pub gpu_utilization: Option,
/// Whether the process is running.
pub is_running: bool,
/// Max total memory in bytes that was ever allocated by the process.
///
/// It will be `None` if `ProcessInfo.used_gpu_memory` is not supported.
pub max_memory_usage: Option,
/**
Percent of time over the process's lifetime during which global (device) memory
was being read from or written to.
It will be `None` if `Device.utilization_rates()` is not supported.
*/
pub memory_utilization: Option,
/// CPU timestamp in usec representing the start time for the process.
pub start_time: u64,
/// Amount of time in ms during which the compute context was active. This
/// will be zero if the process is not terminated.
pub time: u64,
}
impl From for AccountingStats {
fn from(struct_: nvmlAccountingStats_t) -> Self {
let not_avail_u64 = (NVML_VALUE_NOT_AVAILABLE) as u64;
let not_avail_u32 = (NVML_VALUE_NOT_AVAILABLE) as u32;
#[allow(clippy::match_like_matches_macro)]
Self {
gpu_utilization: match struct_.gpuUtilization {
v if v == not_avail_u32 => None,
_ => Some(struct_.gpuUtilization),
},
is_running: match struct_.isRunning {
0 => false,
// NVIDIA only says 1 is for running, but I don't think anything
// else warrants an error (or a panic), so
_ => true,
},
max_memory_usage: match struct_.maxMemoryUsage {
v if v == not_avail_u64 => None,
_ => Some(struct_.maxMemoryUsage),
},
memory_utilization: match struct_.memoryUtilization {
v if v == not_avail_u32 => None,
_ => Some(struct_.memoryUtilization),
},
start_time: struct_.startTime,
time: struct_.time,
}
}
}
/// Holds encoder session information.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EncoderSessionInfo {
/// Unique ID for this session.
pub session_id: u32,
/// The ID of the process that owns this session.
pub pid: u32,
/// The ID of the vGPU instance that owns this session (if applicable).
// TODO: Stronger typing if vgpu stuff gets wrapped
pub vgpu_instance: Option,
pub codec_type: EncoderType,
/// Current horizontal encoding resolution.
pub hres: u32,
/// Current vertical encoding resolution.
pub vres: u32,
/// Moving average encode frames per second.
pub average_fps: u32,
/// Moving average encode latency in μs.
pub average_latency: u32,
}
impl TryFrom for EncoderSessionInfo {
type Error = NvmlError;
/**
Construct `EncoderSessionInfo` from the corresponding C struct.
# Errors
* `UnexpectedVariant`, for which you can read the docs for
*/
fn try_from(value: nvmlEncoderSessionInfo_t) -> Result {
Ok(Self {
session_id: value.sessionId,
pid: value.pid,
vgpu_instance: match value.vgpuInstance {
0 => None,
other => Some(other),
},
codec_type: EncoderType::try_from(value.codecType)?,
hres: value.hResolution,
vres: value.vResolution,
average_fps: value.averageFps,
average_latency: value.averageLatency,
})
}
}
/// Sample info.
// Checked against local
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Sample {
/// CPU timestamp in μs
pub timestamp: u64,
pub value: SampleValue,
}
impl Sample {
/// Given a tag and an untagged union, returns a Rust enum with the correct
/// union variant.
pub fn from_tag_and_struct(tag: &SampleValueType, struct_: nvmlSample_t) -> Self {
Self {
timestamp: struct_.timeStamp,
value: SampleValue::from_tag_and_union(tag, struct_.sampleValue),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ProcessUtilizationSample {
pub pid: u32,
/// CPU timestamp in μs
pub timestamp: u64,
/// SM (3D / compute) utilization
pub sm_util: u32,
/// Frame buffer memory utilization
pub mem_util: u32,
/// Encoder utilization
pub enc_util: u32,
/// Decoder utilization
pub dec_util: u32,
}
impl From for ProcessUtilizationSample {
fn from(struct_: nvmlProcessUtilizationSample_t) -> Self {
Self {
pid: struct_.pid,
timestamp: struct_.timeStamp,
sm_util: struct_.smUtil,
mem_util: struct_.memUtil,
enc_util: struct_.encUtil,
dec_util: struct_.decUtil,
}
}
}
/// Struct that stores information returned from `Device.field_values_for()`.
// TODO: Missing a lot of derives because of the `Result`
#[derive(Debug)]
pub struct FieldValueSample {
/// The field that this sample is for.
pub field: FieldId,
/// This sample's CPU timestamp in μs (Unix time).
pub timestamp: i64,
/**
How long this field value took to update within NVML, in μs.
This value may be averaged across several fields serviced by the same
driver call.
*/
pub latency: i64,
/// The value of this sample.
///
/// Will be an error if retrieving this specific value failed.
pub value: Result,
}
impl TryFrom for FieldValueSample {
type Error = NvmlError;
/**
Construct `FieldValueSample` from the corresponding C struct.
# Errors
* `UnexpectedVariant`, for which you can read the docs for
*/
fn try_from(value: nvmlFieldValue_t) -> Result {
Ok(Self {
field: FieldId(value.fieldId),
timestamp: value.timestamp,
latency: value.latencyUsec,
value: match nvml_try(value.nvmlReturn) {
Ok(_) => Ok(SampleValue::from_tag_and_union(
&SampleValueType::try_from(value.valueType)?,
value.value,
)),
Err(e) => Err(e),
},
})
}
}
/// Holds global frame buffer capture session statistics.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FbcStats {
/// The total number of sessions
pub sessions_count: u32,
/// Moving average of new frames captured per second for all capture sessions
pub average_fps: u32,
/// Moving average of new frame capture latency in microseconds for all capture sessions
pub average_latency: u32,
}
impl From for FbcStats {
fn from(struct_: nvmlFBCStats_t) -> Self {
Self {
sessions_count: struct_.sessionsCount,
average_fps: struct_.averageFPS,
average_latency: struct_.averageLatency,
}
}
}
/// Information about a frame buffer capture session.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FbcSessionInfo {
/// Unique session ID
pub session_id: u32,
/// The ID of the process that owns this session
pub pid: u32,
/// The ID of the vGPU instance that owns this session (if applicable).
// TODO: Stronger typing if vgpu stuff gets wrapped
pub vgpu_instance: Option,
/// The identifier of the display this session is running on
pub display_ordinal: u32,
/// The type of this session
pub session_type: FbcSessionType,
/// Various flags with info
pub session_flags: FbcFlags,
/// The maximum horizontal resolution supported by this session
pub hres_max: u32,
/// The maximum vertical resolution supported by this session
pub vres_max: u32,
/// The horizontal resolution requested by the caller in the capture call
pub hres: u32,
/// The vertical resolution requested by the caller in the capture call
pub vres: u32,
/// Moving average of new frames captured per second for this session
pub average_fps: u32,
/// Moving average of new frame capture latency in microseconds for this session
pub average_latency: u32,
}
impl TryFrom