nvml-wrapper-0.10.0/Cargo.lock0000644000000214100000000000100115150ustar # 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.toml0000644000000030450000000000100115440ustar # 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.orig000064400000000000000000000017740072674642500152640ustar 00000000000000[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.md000064400000000000000000000107520072674642500136500ustar 00000000000000# nvml-wrapper [![Docs.rs docs](https://docs.rs/nvml-wrapper/badge.svg)](https://docs.rs/nvml-wrapper) [![Crates.io version](https://img.shields.io/crates/v/nvml-wrapper.svg?style=flat-square)](https://crates.io/crates/nvml-wrapper) [![Crates.io downloads](https://img.shields.io/crates/d/nvml-wrapper.svg?style=flat-square)](https://crates.io/crates/nvml-wrapper) ![CI](https://github.com/Cldfire/nvml-wrapper/workflows/CI/badge.svg) [![dependency status](https://deps.rs/repo/github/cldfire/nvml-wrapper/status.svg)](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.rs000064400000000000000000000066310072674642500170230ustar 00000000000000use 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.rs000064400000000000000000000040410072674642500167210ustar 00000000000000#[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.rs000064400000000000000000000106400072674642500165760ustar 00000000000000#![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.rs000064400000000000000000000035030072674642500164600ustar 00000000000000use 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.rs000064400000000000000000000017200072674642500161150ustar 00000000000000pub 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.rs000064400000000000000000000031030072674642500167730ustar 00000000000000use 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.rs000064400000000000000000006267200072674642500147750ustar 00000000000000#[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.rs000064400000000000000000000527530072674642500176630ustar 00000000000000use 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.rs000064400000000000000000000011510072674642500171650ustar 00000000000000use 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.rs000064400000000000000000000046100072674642500200510ustar 00000000000000use 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.rs000064400000000000000000000016730072674642500173760ustar 00000000000000use 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.rs000064400000000000000000000305140072674642500161120ustar 00000000000000use 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.rs000064400000000000000000000007470072674642500160010ustar 00000000000000#[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.rs000064400000000000000000000001020072674642500154200ustar 00000000000000pub mod device; pub mod event; pub mod nv_link; pub mod unit; nvml-wrapper-0.10.0/src/enums/nv_link.rs000064400000000000000000000006230072674642500163110ustar 00000000000000#[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.rs000064400000000000000000000027770072674642500156440ustar 00000000000000use 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.rs000064400000000000000000000157440072674642500146650ustar 00000000000000use 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.rs000064400000000000000000000136360072674642500146530ustar 00000000000000use 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.rs000064400000000000000000000207420072674642500200060ustar 00000000000000/*! 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.rs000064400000000000000000000002210072674642500164010ustar 00000000000000#[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.rs000064400000000000000000001166440072674642500143030ustar 00000000000000/*! 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.rs000064400000000000000000000472070072674642500151730ustar 00000000000000use 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.rs000064400000000000000000000557320072674642500202430ustar 00000000000000use 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 for FbcSessionInfo { type Error = NvmlError; /** Construct `FbcSessionInfo` from the corresponding C struct. # Errors * `UnexpectedVariant`, for which you can read the docs for * `IncorrectBits`, if the `sessionFlags` from the given struct do match the wrapper definition */ fn try_from(value: nvmlFBCSessionInfo_t) -> Result { Ok(Self { session_id: value.sessionId, pid: value.pid, vgpu_instance: match value.vgpuInstance { 0 => None, other => Some(other), }, display_ordinal: value.displayOrdinal, session_type: FbcSessionType::try_from(value.sessionType)?, session_flags: FbcFlags::from_bits(value.sessionFlags) .ok_or(NvmlError::IncorrectBits(Bits::U32(value.sessionFlags)))?, hres_max: value.hMaxResolution, vres_max: value.vMaxResolution, hres: value.hResolution, vres: value.vResolution, average_fps: value.averageFPS, average_latency: value.averageLatency, }) } } #[cfg(test)] #[allow(unused_variables, unused_imports)] mod tests { use crate::error::*; use crate::ffi::bindings::*; use crate::test_utils::*; use std::convert::TryInto; use std::mem; #[test] fn pci_info_from_to_c() { let nvml = nvml(); test_with_device(3, &nvml, |device| { let converted: nvmlPciInfo_t = device .pci_info() .expect("wrapped pci info") .try_into() .expect("converted c pci info"); let sym = nvml_sym(nvml.lib.nvmlDeviceGetPciInfo_v3.as_ref())?; let raw = unsafe { let mut pci_info: nvmlPciInfo_t = mem::zeroed(); nvml_try(sym(device.handle(), &mut pci_info)).expect("raw pci info"); pci_info }; assert_eq!(converted.busId, raw.busId); assert_eq!(converted.domain, raw.domain); assert_eq!(converted.bus, raw.bus); assert_eq!(converted.device, raw.device); assert_eq!(converted.pciDeviceId, raw.pciDeviceId); assert_eq!(converted.pciSubSystemId, raw.pciSubSystemId); Ok(()) }) } } nvml-wrapper-0.10.0/src/struct_wrappers/event.rs000064400000000000000000000040450072674642500201140ustar 00000000000000use crate::device::Device; use crate::enums::event::XidError; use crate::ffi::bindings::*; use crate::{bitmasks::event::EventTypes, Nvml}; /// Information about an event that has occurred. // Checked against local #[derive(Debug)] pub struct EventData<'nvml> { /** Device where the event occurred. See `Device.uuid()` for a way to compare this `Device` to another `Device` and find out if they represent the same physical device. */ pub device: Device<'nvml>, /// Information about what specific event occurred. pub event_type: EventTypes, /** Stores the last XID error for the device for the nvmlEventTypeXidCriticalError event. `None` in the case of any other event type. */ pub event_data: Option, } impl<'nvml> EventData<'nvml> { /** Create a new `EventData` wrapper. The `event_type` 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. # Safety It is your responsibility to ensure that the given `nvmlEventdata_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(event_data: nvmlEventData_t, nvml: &'nvml Nvml) -> Self { let event_type = EventTypes::from_bits_truncate(event_data.eventType); EventData { // SAFETY: it is the callers responsibility to ensure that `event_data` // is a valid pointer (meaning its contents will be valid) device: Device::new(event_data.device, nvml), event_type, event_data: if event_type.contains(EventTypes::CRITICAL_XID_ERROR) { Some(match event_data.eventData { 999 => XidError::Unknown, v => XidError::Value(v), }) } else { None }, } } } nvml-wrapper-0.10.0/src/struct_wrappers/mod.rs000064400000000000000000000021330072674642500175460ustar 00000000000000pub mod device; pub mod event; pub mod nv_link; pub mod unit; use self::device::PciInfo; use crate::error::NvmlError; use crate::ffi::bindings::*; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; use std::{convert::TryFrom, ffi::CStr}; /// Information about an excluded device. #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ExcludedDeviceInfo { pci_info: PciInfo, uuid: String, } impl TryFrom for ExcludedDeviceInfo { type Error = NvmlError; /** Construct [`ExcludedDeviceInfo`] from the corresponding C struct. # Errors * `UnexpectedVariant`, for which you can read the docs for */ fn try_from(value: nvmlExcludedDeviceInfo_t) -> Result { unsafe { let uuid_raw = CStr::from_ptr(value.uuid.as_ptr()); Ok(Self { pci_info: PciInfo::try_from(value.pciInfo, true)?, uuid: uuid_raw.to_str()?.into(), }) } } } nvml-wrapper-0.10.0/src/struct_wrappers/nv_link.rs000064400000000000000000000031710072674642500204320ustar 00000000000000use crate::bitmasks::nv_link::PacketTypes; use crate::enum_wrappers::nv_link::UtilizationCountUnit; use crate::error::NvmlError; use crate::ffi::bindings::*; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; use std::convert::TryFrom; /// Defines NvLink counter controls. // TODO: Write a test going to / from C repr #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct UtilizationControl { pub units: UtilizationCountUnit, pub packet_filter: PacketTypes, } impl UtilizationControl { /// Obtain this struct's C counterpart. pub fn as_c(&self) -> nvmlNvLinkUtilizationControl_t { nvmlNvLinkUtilizationControl_t { units: self.units.as_c(), pktfilter: self.packet_filter.bits(), } } } impl TryFrom for UtilizationControl { type Error = NvmlError; /** Construct `UtilizationControl` from the corresponding C struct. The `packet_filter` bitmask is created via the `PacketTypes::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 * `UnexpectedVariant`, for which you can read the docs for */ fn try_from(value: nvmlNvLinkUtilizationControl_t) -> Result { let bits = value.pktfilter; Ok(UtilizationControl { units: UtilizationCountUnit::try_from(value.units)?, packet_filter: PacketTypes::from_bits_truncate(bits), }) } } nvml-wrapper-0.10.0/src/struct_wrappers/unit.rs000064400000000000000000000125170072674642500177550ustar 00000000000000use crate::enum_wrappers::unit::FanState; use crate::error::NvmlError; use crate::ffi::bindings::*; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; use std::{convert::TryFrom, ffi::CStr}; /// Fan information readings for an entire S-class unit. // Checked against local #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct FansInfo { /// Number of fans in the unit. pub count: u32, /// Fan data for each fan. pub fans: Vec, } impl TryFrom for FansInfo { type Error = NvmlError; /** Construct `FansInfo` from the corresponding C struct. # Errors * `UnexpectedVariant`, for which you can read the docs for */ fn try_from(value: nvmlUnitFanSpeeds_t) -> Result { let fans = value .fans .iter() .map(|f| FanInfo::try_from(*f)) .collect::>()?; Ok(FansInfo { count: value.count, fans, }) } } /// Fan info reading for a single fan in an S-class unit. // Checked against local #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct FanInfo { /// Fan speed (RPM). pub speed: u32, /// Indicates whether a fan is working properly. pub state: FanState, } impl TryFrom for FanInfo { type Error = NvmlError; /** Construct `FanInfo` from the corresponding C struct. # Errors * `UnexpectedVariant`, for which you can read the docs for */ fn try_from(value: nvmlUnitFanInfo_t) -> Result { Ok(FanInfo { speed: value.speed, state: FanState::try_from(value.state)?, }) } } /** Power usage information for an S-class unit. The power supply state is a human-readable string that equals "Normal" or contains a combination of "Abnormal" plus one or more of the following (aka good luck matching on it): * High voltage * Fan failure * Heatsink temperature * Current limit * Voltage below UV alarm threshold * Low-voltage * SI2C remote off command * MOD_DISABLE input * Short pin transition */ // Checked against local #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PsuInfo { /// PSU current (in A) pub current: u32, /// PSU power draw (in W) pub power_draw: u32, /// Human-readable string describing the PSU state. pub state: String, /// PSU voltage (in V) pub voltage: u32, } impl TryFrom for PsuInfo { type Error = NvmlError; /** Construct `PsuInfo` from the corresponding C struct. # Errors * `UnexpectedVariant`, for which you can read the docs for */ fn try_from(value: nvmlPSUInfo_t) -> Result { unsafe { let state_raw = CStr::from_ptr(value.state.as_ptr()); Ok(PsuInfo { current: value.current, power_draw: value.power, state: state_raw.to_str()?.into(), voltage: value.voltage, }) } } } /// Static S-class unit info. // Checked against local #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct UnitInfo { pub firmware_version: String, /// Product identifier. pub id: String, pub name: String, /// Product serial number. pub serial: String, } impl TryFrom for UnitInfo { type Error = NvmlError; /** Construct `UnitInfo` from the corresponding C struct. # Errors * `UnexpectedVariant`, for which you can read the docs for */ fn try_from(value: nvmlUnitInfo_t) -> Result { unsafe { let version_raw = CStr::from_ptr(value.firmwareVersion.as_ptr()); let id_raw = CStr::from_ptr(value.id.as_ptr()); let name_raw = CStr::from_ptr(value.name.as_ptr()); let serial_raw = CStr::from_ptr(value.serial.as_ptr()); Ok(UnitInfo { firmware_version: version_raw.to_str()?.into(), id: id_raw.to_str()?.into(), name: name_raw.to_str()?.into(), serial: serial_raw.to_str()?.into(), }) } } } /// Description of an HWBC entry. // Checked against local #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct HwbcEntry { pub id: u32, pub firmware_version: String, } impl TryFrom for HwbcEntry { type Error = NvmlError; /** Construct `HwbcEntry` from the corresponding C struct. # Errors * `UnexpectedVariant`, for which you can read the docs for */ fn try_from(value: nvmlHwbcEntry_t) -> Result { unsafe { let version_raw = CStr::from_ptr(value.firmwareVersion.as_ptr()); Ok(HwbcEntry { id: value.hwbcId, firmware_version: version_raw.to_str()?.into(), }) } } } nvml-wrapper-0.10.0/src/structs/device.rs000064400000000000000000000066720072674642500165020ustar 00000000000000#[cfg(target_os = "windows")] use crate::enum_wrappers::device::DriverModel; use crate::enum_wrappers::device::OperationMode; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; /// Returned from `Device.auto_boosted_clocks_enabled()` #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct AutoBoostClocksEnabledInfo { /// Current state of auto boosted clocks for the `Device` pub is_enabled: bool, /// Default auto boosted clocks behavior for the `Device` /// /// The GPU will revert to this default when no applications are using the /// GPU. pub is_enabled_default: bool, } /// Returned from `Device.decoder_utilization()` and /// `Device.encoder_utilization()`. #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct UtilizationInfo { pub utilization: u32, /// Sampling period in μs. pub sampling_period: u32, } /// Returned from `Device.driver_model()` #[derive(Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg(target_os = "windows")] pub struct DriverModelState { pub current: DriverModel, pub pending: DriverModel, } /// Returned from `Device.is_ecc_enabled()` #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct EccModeState { pub currently_enabled: bool, pub pending_enabled: bool, } /// Returned from `Device.gpu_operation_mode()` #[derive(Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct OperationModeState { pub current: OperationMode, pub pending: OperationMode, } /// Returned from `Device.power_management_limit_constraints()` /// /// Values are in milliwatts. #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PowerManagementConstraints { pub min_limit: u32, pub max_limit: u32, } /// Returned from `Device.encoder_stats()` #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct EncoderStats { /// The number of active encoder sessions. pub session_count: u32, /// The trailing average FPS of all active encoder sessions. pub average_fps: u32, /// The encode latency in μs. pub average_latency: u32, } /// Returned from `Device.cuda_compute_capability()` #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct CudaComputeCapability { pub major: i32, pub minor: i32, } /// Returned from `Device.retired_pages()` #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RetiredPage { /// The hardware address of the page that was retired. /// /// Note that this does not match the virtual address used in CUDA but does /// match the address information in XID 63. pub address: u64, /// The retirement timestamp. pub timestamp: u64, } /// Populate this newtype with the constants `nvml_wrapper::sys_exports::field_id::*`. /// /// Used in `FieldValue` and `Device.field_values_for()`. #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct FieldId(pub u32); nvml-wrapper-0.10.0/src/structs/mod.rs000064400000000000000000000000430072674642500160040ustar 00000000000000pub mod device; pub mod nv_link; nvml-wrapper-0.10.0/src/structs/nv_link.rs000064400000000000000000000005620072674642500166730ustar 00000000000000#[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; /// Returned by `NvLink.utilization_counter()` #[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct UtilizationCounter { /// Receive counter value pub receive: u64, /// Send counter value pub send: u64, } nvml-wrapper-0.10.0/src/test_utils.rs000064400000000000000000000117270072674642500157300ustar 00000000000000use crate::Device; use crate::NvLink; use crate::Nvml; use crate::Unit; use crate::bitmasks::{device::*, event::*}; use crate::enum_wrappers::device::*; use crate::enums::device::BusType; use crate::enums::device::DeviceArchitecture; use crate::enums::device::PcieLinkMaxSpeed; use crate::enums::device::PowerSource; use crate::enums::unit::*; use crate::error::NvmlError; use crate::event::EventSet; use std::fmt::Debug; use crate::struct_wrappers::nv_link::*; use crate::struct_wrappers::{device::*, event::*, unit::*, *}; use crate::structs::device::*; use crate::structs::nv_link::*; #[cfg(target_os = "windows")] use crate::structs::device::DriverModelState; pub trait ShouldPrint: Debug { fn should_print(&self) -> bool { true } } impl ShouldPrint for () { fn should_print(&self) -> bool { false } } impl<'nvml> ShouldPrint for Device<'nvml> { fn should_print(&self) -> bool { false } } impl<'nvml> ShouldPrint for Unit<'nvml> { fn should_print(&self) -> bool { false } } impl<'nvml> ShouldPrint for EventSet<'nvml> { fn should_print(&self) -> bool { false } } impl ShouldPrint for bool {} impl ShouldPrint for u32 {} impl ShouldPrint for i32 {} impl ShouldPrint for (u32, u32) {} impl ShouldPrint for u64 {} impl ShouldPrint for String {} impl ShouldPrint for Brand {} impl ShouldPrint for [i8; 16] {} impl ShouldPrint for Vec {} impl ShouldPrint for Vec {} impl<'nvml> ShouldPrint for Vec> {} impl ShouldPrint for Vec {} impl ShouldPrint for Vec {} impl ShouldPrint for Vec {} impl ShouldPrint for Vec> {} impl ShouldPrint for Vec {} impl ShouldPrint for Utilization {} impl ShouldPrint for EncoderStats {} impl ShouldPrint for FbcStats {} impl ShouldPrint for Vec {} impl ShouldPrint for Vec {} impl ShouldPrint for AutoBoostClocksEnabledInfo {} impl ShouldPrint for BAR1MemoryInfo {} impl ShouldPrint for BridgeChipHierarchy {} impl ShouldPrint for ComputeMode {} impl ShouldPrint for UtilizationInfo {} impl ShouldPrint for EccModeState {} impl ShouldPrint for OperationModeState {} impl ShouldPrint for InfoRom {} impl ShouldPrint for Vec {} impl ShouldPrint for ExcludedDeviceInfo {} impl ShouldPrint for MemoryInfo {} impl ShouldPrint for PciInfo {} impl ShouldPrint for PerformanceState {} impl ShouldPrint for PowerManagementConstraints {} impl ShouldPrint for ThrottleReasons {} impl ShouldPrint for ViolationTime {} impl ShouldPrint for AccountingStats {} impl ShouldPrint for EventTypes {} impl<'nvml> ShouldPrint for EventData<'nvml> {} impl ShouldPrint for FansInfo {} impl ShouldPrint for LedState {} impl ShouldPrint for PsuInfo {} impl ShouldPrint for UnitInfo {} impl ShouldPrint for UtilizationControl {} impl ShouldPrint for UtilizationCounter {} impl ShouldPrint for BusType {} impl ShouldPrint for PowerSource {} impl ShouldPrint for DeviceArchitecture {} impl ShouldPrint for PcieLinkMaxSpeed {} #[cfg(target_os = "windows")] impl ShouldPrint for DriverModelState {} pub fn nvml() -> Nvml { Nvml::init().expect("initialized library") } pub fn device(nvml: &Nvml) -> Device<'_> { nvml.device_by_index(0).expect("device") } pub fn unit(nvml: &Nvml) -> Unit<'_> { nvml.unit_by_index(0).expect("unit") } /// Run all testing methods for the given test. pub fn test(reps: usize, test: T) where T: Fn() -> Result, R: ShouldPrint, { single(|| test()); multi(reps, || test()); } pub fn test_with_device(reps: usize, nvml: &Nvml, test: T) where T: Fn(&Device) -> Result, R: ShouldPrint, { let device = device(nvml); single(|| test(&device)); multi(reps, || test(&device)); } pub fn test_with_unit(reps: usize, nvml: &Nvml, test: T) where T: Fn(&Unit) -> Result, R: ShouldPrint, { let unit = unit(nvml); single(|| test(&unit)); multi(reps, || test(&unit)); } pub fn test_with_link(reps: usize, nvml: &Nvml, test: T) where T: Fn(&NvLink) -> Result, R: ShouldPrint, { // Is 0 a good default??? let device = device(nvml); let link = device.link_wrapper_for(0); single(|| test(&link)); multi(reps, || test(&link)); } /// Run the given test once. pub fn single(test: T) where T: Fn() -> Result, R: ShouldPrint, { let res = test().expect("successful single test"); if res.should_print() { print!("{:?} ... ", res); } } /// Run the given test multiple times. pub fn multi(count: usize, test: T) where T: Fn() -> Result, R: ShouldPrint, { for i in 0..count { test().unwrap_or_else(|_| panic!("successful multi call #{}", i)); } } nvml-wrapper-0.10.0/src/unit.rs000064400000000000000000000305160072674642500145050ustar 00000000000000use crate::device::Device; use crate::enum_wrappers::unit::LedColor; use crate::enums::unit::{LedState, TemperatureReading}; use crate::error::{nvml_sym, nvml_try, NvmlError}; use crate::ffi::bindings::*; use crate::struct_wrappers::unit::{FansInfo, PsuInfo, UnitInfo}; use crate::Nvml; use static_assertions::assert_impl_all; use std::mem; use std::{convert::TryFrom, os::raw::c_uint}; /** Struct that represents a unit. Obtain a `Unit` with the various methods available to you on the `Nvml` struct. Lifetimes are used to enforce that each `Unit` 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 unit = nvml.unit_by_index(0)?; drop(nvml); // This won't compile let unit_devices = unit.devices()?; # Ok(()) # } ``` Note that I cannot test any `Unit` methods myself as I do not have access to such hardware. **Test the functionality in this module before you use it**. */ #[derive(Debug)] pub struct Unit<'nvml> { unit: nvmlUnit_t, nvml: &'nvml Nvml, } unsafe impl<'nvml> Send for Unit<'nvml> {} unsafe impl<'nvml> Sync for Unit<'nvml> {} assert_impl_all!(Unit: Send, Sync); impl<'nvml> Unit<'nvml> { /** Create a new `Unit` 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 `nvmlUnit_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(unit: nvmlUnit_t, nvml: &'nvml Nvml) -> Self { Self { unit, nvml } } /// Access the `NVML` reference this struct wraps pub fn nvml(&self) -> &'nvml Nvml { self.nvml } /// Get the raw unit 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) -> nvmlUnit_t { self.unit } /** Gets the set of GPU devices that are attached to this `Unit`. **I do not have the hardware to test this call. Verify for yourself that it works before you use it**. If it works, please let me know; if it doesn't, I would love a PR. If NVML is sane this should work, but NVIDIA's docs on this call are _anything_ but clear. # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `Unknown`, on any unexpected error # Device Support For S-class products. */ // Checked against local // Tested #[doc(alias = "nvmlUnitGetDevices")] pub fn devices(&self) -> Result, NvmlError> { let sym = nvml_sym(self.nvml.lib.nvmlUnitGetDevices.as_ref())?; unsafe { let mut count: c_uint = match self.device_count()? { 0 => return Ok(vec![]), value => value, }; let mut devices: Vec = vec![mem::zeroed(); count as usize]; nvml_try(sym(self.unit, &mut count, devices.as_mut_ptr()))?; Ok(devices .into_iter() .map(|d| Device::new(d, self.nvml)) .collect()) } } /** Gets the count of GPU devices that are attached to this `Unit`. **I do not have the hardware to test this call. Verify for yourself that it works before you use it**. If it works, please let me know; if it doesn't, I would love a PR. If NVML is sane this should work, but NVIDIA's docs on this call are _anything_ but clear. # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `Unknown`, on any unexpected error # Device Support For S-class products. */ // Tested as part of the above #[doc(alias = "nvmlUnitGetDevices")] pub fn device_count(&self) -> Result { let sym = nvml_sym(self.nvml.lib.nvmlUnitGetDevices.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 devices, NVML_SUCCESS is returned, `count` is set to 0. We return count, all good. If there is 1 device, NVML_SUCCESS is returned, `count` is set to 1. We return count, all good. If there are >= 2 devices, 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 devices: [nvmlDevice_t; 1] = [mem::zeroed()]; match sym(self.unit, &mut count, devices.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 fan information for this `Unit` (fan count and state + speed for each). # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `NotSupported`, if this is not an S-class product * `UnexpectedVariant`, for which you can read the docs for * `Unknown`, on any unexpected error # Device Support For S-class products. */ // Checked against local // Tested #[doc(alias = "nvmlUnitGetFanSpeedInfo")] pub fn fan_info(&self) -> Result { let sym = nvml_sym(self.nvml.lib.nvmlUnitGetFanSpeedInfo.as_ref())?; unsafe { let mut fans_info: nvmlUnitFanSpeeds_t = mem::zeroed(); nvml_try(sym(self.unit, &mut fans_info))?; FansInfo::try_from(fans_info) } } /** Gets the LED state associated with this `Unit`. # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `NotSupported`, if this is not an S-class product * `Utf8Error`, if the string obtained from the C function is not valid Utf8 * `Unknown`, on any unexpected error # Device Support For S-class products. */ // Checked against local // Tested #[doc(alias = "nvmlUnitGetLedState")] pub fn led_state(&self) -> Result { let sym = nvml_sym(self.nvml.lib.nvmlUnitGetLedState.as_ref())?; unsafe { let mut state: nvmlLedState_t = mem::zeroed(); nvml_try(sym(self.unit, &mut state))?; LedState::try_from(state) } } /** Gets the PSU stats for this `Unit`. # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `NotSupported`, if this is not an S-class product * `Utf8Error`, if the string obtained from the C function is not valid Utf8 * `Unknown`, on any unexpected error # Device Support For S-class products. */ // Checked against local // Tested #[doc(alias = "nvmlUnitGetPsuInfo")] pub fn psu_info(&self) -> Result { let sym = nvml_sym(self.nvml.lib.nvmlUnitGetPsuInfo.as_ref())?; unsafe { let mut info: nvmlPSUInfo_t = mem::zeroed(); nvml_try(sym(self.unit, &mut info))?; PsuInfo::try_from(info) } } /** Gets the temperature for the specified `UnitTemperatureReading`, in °C. Available readings depend on the product. # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `NotSupported`, if this is not an S-class product * `Unknown`, on any unexpected error # Device Support For S-class products. Available readings depend on the product. */ // Checked against local // Tested #[doc(alias = "nvmlUnitGetTemperature")] pub fn temperature(&self, reading_type: TemperatureReading) -> Result { let sym = nvml_sym(self.nvml.lib.nvmlUnitGetTemperature.as_ref())?; unsafe { let mut temp: c_uint = mem::zeroed(); nvml_try(sym(self.unit, reading_type as c_uint, &mut temp))?; Ok(temp) } } /** Gets the static information associated with this `Unit`. # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `Utf8Error`, if the string obtained from the C function is not valid Utf8 # Device Support For S-class products. */ // Checked against local // Tested #[doc(alias = "nvmlUnitGetUnitInfo")] pub fn info(&self) -> Result { let sym = nvml_sym(self.nvml.lib.nvmlUnitGetUnitInfo.as_ref())?; unsafe { let mut info: nvmlUnitInfo_t = mem::zeroed(); nvml_try(sym(self.unit, &mut info))?; UnitInfo::try_from(info) } } // Unit commands starting here /** Sets the LED color for this `Unit`. Requires root/admin permissions. This operation takes effect immediately. Note: Current S-class products don't provide unique LEDs for each unit. As such, both front and back LEDs will be toggled in unison regardless of which unit is specified with this method (aka the `Unit` represented by this struct). # Errors * `Uninitialized`, if the library has not been successfully initialized * `InvalidArg`, if the unit is invalid * `NotSupported`, if this is not an S-class product * `NoPermission`, if the user doesn't have permission to perform this operation * `Unknown`, on any unexpected error # Device Support For S-class products. */ // checked against local // Tested (no-run) #[doc(alias = "nvmlUnitSetLedState")] pub fn set_led_color(&mut self, color: LedColor) -> Result<(), NvmlError> { let sym = nvml_sym(self.nvml.lib.nvmlUnitSetLedState.as_ref())?; unsafe { nvml_try(sym(self.unit, color.as_c())) } } } // I do not have access to this hardware and cannot test anything #[cfg(test)] #[deny(unused_mut)] mod test { use crate::enum_wrappers::unit::LedColor; use crate::enums::unit::TemperatureReading; use crate::test_utils::*; #[test] #[ignore = "my machine does not support this call"] fn devices() { let nvml = nvml(); let unit = unit(&nvml); unit.devices().expect("devices"); } #[test] #[ignore = "my machine does not support this call"] fn fan_info() { let nvml = nvml(); test_with_unit(3, &nvml, |unit| unit.fan_info()) } #[test] #[ignore = "my machine does not support this call"] fn led_state() { let nvml = nvml(); test_with_unit(3, &nvml, |unit| unit.led_state()) } #[test] #[ignore = "my machine does not support this call"] fn psu_info() { let nvml = nvml(); test_with_unit(3, &nvml, |unit| unit.psu_info()) } #[test] #[ignore = "my machine does not support this call"] fn temperature() { let nvml = nvml(); test_with_unit(3, &nvml, |unit| unit.temperature(TemperatureReading::Board)) } #[test] #[ignore = "my machine does not support this call"] fn info() { let nvml = nvml(); test_with_unit(3, &nvml, |unit| unit.info()) } // This modifies unit state, so we don't want to actually run the test #[allow(dead_code)] fn set_led_color() { let nvml = nvml(); let mut unit = unit(&nvml); unit.set_led_color(LedColor::Amber).expect("set to true") } } nvml-wrapper-0.10.0/unwrapped_functions.sh000064400000000000000000000035470072674642500170260ustar 00000000000000#!/bin/bash # Writes all unwrapped function names to `unwrapped_functions.txt`. This can # help discover functions to work on wrapping. # # `ripgrep` must be installed and available. `cargo install ripgrep` all_functions=$(rg 'pub unsafe fn (\w+)' -oNr '$1' ../nvml-wrapper-sys/src/bindings.rs | sort) readarray -t all_functions_arr <<< "$all_functions" output="" versioned_output="" for name in "${all_functions_arr[@]}" do if [[ $name = "new" ]] then continue fi # filter out function names that appear in the wrapper source if ! rg -U "lib[ \n]*\.${name}[ \n]*\." -q src/* ; then # some functions are versioned in the format {name}_v{x} # # this gets {name} only for every function name unversioned_name=$(echo "${name}" | cut -d "_" -f 1) # take this unversioned function name (does not end in _vx) and look # for any function with the same name in the wrapper source (may or may # not end in _vx). # # if we find anything here we know this function is part of a series of # versioned functions. Output it separately. if rg -U "lib[ \n]*\.${unversioned_name}(_v.)?[ \n]*\." -q src/* ; then versioned_output+="${name}" versioned_output+=$'\n' else output+="${name}" output+=$'\n' fi fi done # heredoc to write multi-line string to file cat > unwrapped_functions.txt <<- EndOfMessage $output the following functions are part of a series of versioned functions, at least one of which appears in the wrapper source code. this means some version is already wrapped and the listed names are either newer versions to be wrapped or older versions that could be wrapped behind the legacy-functions feature. $versioned_output EndOfMessage nvml-wrapper-0.10.0/unwrapped_functions.txt000064400000000000000000000145410072674642500172270ustar 00000000000000from_library nvmlComputeInstanceDestroy nvmlComputeInstanceGetInfo nvmlComputeInstanceGetInfo_v2 nvmlDeviceClearFieldValues nvmlDeviceCreateGpuInstance nvmlDeviceCreateGpuInstanceWithPlacement nvmlDeviceFreezeNvLinkUtilizationCounter nvmlDeviceGetActiveVgpus nvmlDeviceGetAdaptiveClockInfoStatus nvmlDeviceGetAttributes nvmlDeviceGetAttributes_v2 nvmlDeviceGetClkMonStatus nvmlDeviceGetComputeInstanceId nvmlDeviceGetComputeRunningProcesses nvmlDeviceGetComputeRunningProcesses_v2 nvmlDeviceGetComputeRunningProcesses_v3 nvmlDeviceGetConfComputeGpuAttestationReport nvmlDeviceGetConfComputeGpuCertificate nvmlDeviceGetConfComputeMemSizeInfo nvmlDeviceGetConfComputeProtectedMemoryUsage nvmlDeviceGetCpuAffinityWithinScope nvmlDeviceGetCreatableVgpus nvmlDeviceGetCurrentClocksEventReasons nvmlDeviceGetCurrentClocksThrottleReasons nvmlDeviceGetDefaultEccMode nvmlDeviceGetDeviceHandleFromMigDeviceHandle nvmlDeviceGetDynamicPstatesInfo nvmlDeviceGetFanControlPolicy_v2 nvmlDeviceGetGpcClkMinMaxVfOffset nvmlDeviceGetGpcClkVfOffset nvmlDeviceGetGpuFabricInfo nvmlDeviceGetGpuInstanceById nvmlDeviceGetGpuInstanceId nvmlDeviceGetGpuInstancePossiblePlacements nvmlDeviceGetGpuInstancePossiblePlacements_v2 nvmlDeviceGetGpuInstanceProfileInfo nvmlDeviceGetGpuInstanceProfileInfoV nvmlDeviceGetGpuInstanceRemainingCapacity nvmlDeviceGetGpuInstances nvmlDeviceGetGpuMaxPcieLinkGeneration nvmlDeviceGetGraphicsRunningProcesses nvmlDeviceGetGraphicsRunningProcesses_v2 nvmlDeviceGetGraphicsRunningProcesses_v3 nvmlDeviceGetGridLicensableFeatures nvmlDeviceGetGridLicensableFeatures_v2 nvmlDeviceGetGridLicensableFeatures_v3 nvmlDeviceGetGridLicensableFeatures_v4 nvmlDeviceGetGspFirmwareMode nvmlDeviceGetGspFirmwareVersion nvmlDeviceGetHostVgpuMode nvmlDeviceGetInforomConfigurationChecksum nvmlDeviceGetJpgUtilization nvmlDeviceGetMPSComputeRunningProcesses nvmlDeviceGetMPSComputeRunningProcesses_v2 nvmlDeviceGetMPSComputeRunningProcesses_v3 nvmlDeviceGetMaxMigDeviceCount nvmlDeviceGetMemClkMinMaxVfOffset nvmlDeviceGetMemClkVfOffset nvmlDeviceGetMemoryAffinity nvmlDeviceGetMigDeviceHandleByIndex nvmlDeviceGetMigMode nvmlDeviceGetMinMaxClockOfPState nvmlDeviceGetMinMaxFanSpeed nvmlDeviceGetModuleId nvmlDeviceGetNvLinkCapability nvmlDeviceGetNvLinkErrorCounter nvmlDeviceGetNvLinkRemoteDeviceType nvmlDeviceGetNvLinkRemotePciInfo nvmlDeviceGetNvLinkRemotePciInfo_v2 nvmlDeviceGetNvLinkUtilizationControl nvmlDeviceGetNvLinkUtilizationCounter nvmlDeviceGetOfaUtilization nvmlDeviceGetP2PStatus nvmlDeviceGetPgpuMetadataString nvmlDeviceGetPowerManagementDefaultLimit nvmlDeviceGetPowerManagementLimitConstraints nvmlDeviceGetRemappedRows nvmlDeviceGetRetiredPagesPendingStatus nvmlDeviceGetRowRemapperHistogram nvmlDeviceGetRunningProcessDetailList nvmlDeviceGetSupportedClocksEventReasons nvmlDeviceGetSupportedClocksThrottleReasons nvmlDeviceGetSupportedPerformanceStates nvmlDeviceGetSupportedVgpus nvmlDeviceGetTargetFanSpeed nvmlDeviceGetThermalSettings nvmlDeviceGetVgpuCapabilities nvmlDeviceGetVgpuMetadata nvmlDeviceGetVgpuProcessUtilization nvmlDeviceGetVgpuSchedulerCapabilities nvmlDeviceGetVgpuSchedulerLog nvmlDeviceGetVgpuSchedulerState nvmlDeviceGetVgpuUtilization nvmlDeviceGetVirtualizationMode nvmlDeviceIsMigDeviceHandle nvmlDeviceResetNvLinkErrorCounters nvmlDeviceResetNvLinkUtilizationCounter nvmlDeviceSetConfComputeUnprotectedMemSize nvmlDeviceSetDefaultAutoBoostedClocksEnabled nvmlDeviceSetDefaultFanSpeed_v2 nvmlDeviceSetFanControlPolicy nvmlDeviceSetFanSpeed_v2 nvmlDeviceSetGpcClkVfOffset nvmlDeviceSetMemClkVfOffset nvmlDeviceSetMigMode nvmlDeviceSetNvLinkDeviceLowPowerThreshold nvmlDeviceSetNvLinkUtilizationControl nvmlDeviceSetTemperatureThreshold nvmlDeviceSetVgpuSchedulerState nvmlDeviceSetVirtualizationMode nvmlErrorString nvmlGetVgpuCompatibility nvmlGetVgpuDriverCapabilities nvmlGetVgpuVersion nvmlGpmMetricsGet nvmlGpmMigSampleGet nvmlGpmQueryDeviceSupport nvmlGpmQueryIfStreamingEnabled nvmlGpmSampleAlloc nvmlGpmSampleFree nvmlGpmSampleGet nvmlGpmSetStreamingEnabled nvmlGpuInstanceCreateComputeInstance nvmlGpuInstanceCreateComputeInstanceWithPlacement nvmlGpuInstanceDestroy nvmlGpuInstanceGetComputeInstanceById nvmlGpuInstanceGetComputeInstancePossiblePlacements nvmlGpuInstanceGetComputeInstanceProfileInfo nvmlGpuInstanceGetComputeInstanceProfileInfoV nvmlGpuInstanceGetComputeInstanceRemainingCapacity nvmlGpuInstanceGetComputeInstances nvmlGpuInstanceGetInfo nvmlSetVgpuVersion nvmlSystemGetConfComputeCapabilities nvmlSystemGetConfComputeGpusReadyState nvmlSystemGetConfComputeState nvmlSystemGetNvlinkBwMode nvmlSystemSetConfComputeGpusReadyState nvmlSystemSetNvlinkBwMode nvmlVgpuInstanceClearAccountingPids nvmlVgpuInstanceGetAccountingMode nvmlVgpuInstanceGetAccountingPids nvmlVgpuInstanceGetAccountingStats nvmlVgpuInstanceGetEccMode nvmlVgpuInstanceGetEncoderCapacity nvmlVgpuInstanceGetEncoderSessions nvmlVgpuInstanceGetEncoderStats nvmlVgpuInstanceGetFBCSessions nvmlVgpuInstanceGetFBCStats nvmlVgpuInstanceGetFbUsage nvmlVgpuInstanceGetFrameRateLimit nvmlVgpuInstanceGetGpuInstanceId nvmlVgpuInstanceGetGpuPciId nvmlVgpuInstanceGetLicenseInfo nvmlVgpuInstanceGetLicenseInfo_v2 nvmlVgpuInstanceGetLicenseStatus nvmlVgpuInstanceGetMdevUUID nvmlVgpuInstanceGetMetadata nvmlVgpuInstanceGetType nvmlVgpuInstanceGetUUID nvmlVgpuInstanceGetVmDriverVersion nvmlVgpuInstanceGetVmID nvmlVgpuInstanceSetEncoderCapacity nvmlVgpuTypeGetCapabilities nvmlVgpuTypeGetClass nvmlVgpuTypeGetDeviceID nvmlVgpuTypeGetFrameRateLimit nvmlVgpuTypeGetFramebufferSize nvmlVgpuTypeGetGpuInstanceProfileId nvmlVgpuTypeGetLicense nvmlVgpuTypeGetMaxInstances nvmlVgpuTypeGetMaxInstancesPerVm nvmlVgpuTypeGetName nvmlVgpuTypeGetNumDisplayHeads nvmlVgpuTypeGetResolution the following functions are part of a series of versioned functions, at least one of which appears in the wrapper source code. this means some version is already wrapped and the listed names are either newer versions to be wrapped or older versions that could be wrapped behind the legacy-functions feature. nvmlDeviceGetCount nvmlDeviceGetFanSpeed nvmlDeviceGetHandleByIndex nvmlDeviceGetHandleByPciBusId nvmlDeviceGetMemoryInfo_v2 nvmlDeviceGetPciInfo nvmlDeviceGetPciInfo_v2 nvmlDeviceRemoveGpu nvmlDeviceSetPowerManagementLimit_v2 nvmlEventSetWait nvmlInit nvmlSystemGetCudaDriverVersion