diff --git a/nvml-wrapper/src/device.rs b/nvml-wrapper/src/device.rs index 2e46a24..baa688c 100644 --- a/nvml-wrapper/src/device.rs +++ b/nvml-wrapper/src/device.rs @@ -25,6 +25,8 @@ use crate::ffi::bindings::*; use crate::struct_wrappers::device::*; use crate::structs::device::*; +use crate::vgpu::VgpuType; + #[cfg(target_os = "linux")] use std::convert::TryInto; #[cfg(target_os = "linux")] @@ -5058,6 +5060,48 @@ impl<'nvml> Device<'nvml> { pub fn link_wrapper_for(&self, link: u32) -> NvLink { NvLink { device: self, link } } + + // vGPU + + /// Obtain a list of vGPU type (profiles) supported by the device, if any. + pub fn vgpu_supported_types(&self) -> Result, NvmlError> { + let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetSupportedVgpus.as_ref())?; + let mut ids = vec![]; + + unsafe { + let mut count: c_uint = mem::zeroed(); + + match nvml_try(sym(self.device, &mut count, ids.as_mut_ptr())) { + Ok(()) | Err(NvmlError::InsufficientSize(_)) => {} + Err(err) => return Err(err), + } + + ids.resize(count as usize, 0); + nvml_try(sym(self.device, &mut count, ids.as_mut_ptr()))?; + } + + Ok(ids.into_iter().map(|id| VgpuType::new(self, id)).collect()) + } + + /// Obtain a list of vGPU type (profiles) creatable on the device, if any. + pub fn vgpu_creatable_types(&self) -> Result, NvmlError> { + let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetCreatableVgpus.as_ref())?; + let mut ids = vec![]; + + unsafe { + let mut count: c_uint = mem::zeroed(); + + match nvml_try(sym(self.device, &mut count, ids.as_mut_ptr())) { + Ok(()) | Err(NvmlError::InsufficientSize(_)) => {} + Err(err) => return Err(err), + } + + ids.resize(count as usize, 0); + nvml_try(sym(self.device, &mut count, ids.as_mut_ptr()))?; + } + + Ok(ids.into_iter().map(|id| VgpuType::new(self, id)).collect()) + } } #[cfg(test)] diff --git a/nvml-wrapper/src/lib.rs b/nvml-wrapper/src/lib.rs index 967e66a..084188d 100644 --- a/nvml-wrapper/src/lib.rs +++ b/nvml-wrapper/src/lib.rs @@ -109,6 +109,7 @@ pub mod structs; #[cfg(test)] mod test_utils; pub mod unit; +pub mod vgpu; // Re-exports for convenience pub use crate::device::Device; @@ -297,6 +298,11 @@ impl Nvml { NvmlBuilder::default() } + /// Get the underlying `NvmlLib` instance. + pub fn lib(&self) -> &NvmlLib { + &self.lib + } + /** Use this to shutdown NVML and release allocated resources if you care about handling potential errors (*the `Drop` implementation ignores errors!*). diff --git a/nvml-wrapper/src/vgpu.rs b/nvml-wrapper/src/vgpu.rs new file mode 100644 index 0000000..7fd90be --- /dev/null +++ b/nvml-wrapper/src/vgpu.rs @@ -0,0 +1,357 @@ +use std::ffi::{c_uint, CStr}; + +use ffi::bindings::{ + nvmlVgpuCapability_t, nvmlVgpuTypeId_t, NVML_DEVICE_NAME_BUFFER_SIZE, + NVML_GRID_LICENSE_BUFFER_SIZE, +}; +use static_assertions::assert_impl_all; + +use crate::{ + error::{nvml_sym, nvml_try, NvmlError}, + Device, +}; + +pub struct VgpuType<'dev> { + id: nvmlVgpuTypeId_t, + device: &'dev Device<'dev>, +} + +assert_impl_all!(VgpuType: Send, Sync); + +impl<'dev> VgpuType<'dev> { + /// Create a new vGPU type wrapper. + /// + /// You probably don't need to use this yourself, but rather through + /// [`Device::vgpu_supported_types`] and [`Device::vgpu_creatable_types`]. + pub fn new(device: &'dev Device, id: nvmlVgpuTypeId_t) -> Self { + Self { id, device } + } + + /// Access the `Device` this struct belongs to. + /// + pub fn device(&self) -> &'dev Device { + self.device + } + + /// Retrieve the class of the vGPU type. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetClass")] + pub fn class_name(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetClass.as_ref())?; + + unsafe { + let mut size = NVML_DEVICE_NAME_BUFFER_SIZE; + let mut buffer = vec![0; size as usize]; + + nvml_try(sym(self.id, buffer.as_mut_ptr(), &mut size))?; + + let version_raw = CStr::from_ptr(buffer.as_ptr()); + Ok(version_raw.to_str()?.into()) + } + } + + /// Retrieve license requirements for a vGPU type. + /// + /// The license type and version required to run the specified vGPU type is returned as an + /// alphanumeric string, in the form "\,\", for example + /// "GRID-Virtual-PC,2.0". If a vGPU is runnable with* more than one type of license, the + /// licenses are delimited by a semicolon, for example + /// "GRID-Virtual-PC,2.0;GRID-Virtual-WS,2.0;GRID-Virtual-WS-Ext,2.0". + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InsufficientSize`, if the passed-in `size` is 0 (must be > 0) + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetLicense")] + pub fn license(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetLicense.as_ref())?; + + unsafe { + let mut buffer = vec![0; NVML_GRID_LICENSE_BUFFER_SIZE as usize]; + + nvml_try(sym(self.id, buffer.as_mut_ptr(), buffer.len() as u32))?; + + let version_raw = CStr::from_ptr(buffer.as_ptr()); + Ok(version_raw.to_str()?.into()) + } + } + + /// Retrieve the name of the vGPU type. + /// + /// The name is an alphanumeric string that denotes a particular vGPU, e.g. GRID M60-2Q. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetName")] + pub fn name(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetName.as_ref())?; + + unsafe { + let mut size = NVML_DEVICE_NAME_BUFFER_SIZE; + let mut buffer = vec![0; size as usize]; + + nvml_try(sym(self.id, buffer.as_mut_ptr(), &mut size))?; + + let version_raw = CStr::from_ptr(buffer.as_ptr()); + Ok(version_raw.to_str()?.into()) + } + } + + /// Retrieve the requested capability for a given vGPU type. Refer to the + /// `nvmlVgpuCapability_t` structure for the specific capabilities that can be + /// queried. + + /// # Errors + + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + + /// # Device Support + + /// Maxwell or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetCapabilities")] + pub fn capabilities(&self, capability: nvmlVgpuCapability_t) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetCapabilities.as_ref())?; + + let mut result: c_uint = 0; + unsafe { + nvml_try(sym(self.id, capability, &mut result))?; + } + Ok(result != 0) + } + + /// Retrieve the device ID of the vGPU type. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetDeviceID")] + pub fn device_id(&self) -> Result<(u64, u64), NvmlError> { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetDeviceID.as_ref())?; + + let (mut device_id, mut subsystem_id) = (0, 0); + unsafe { + nvml_try(sym(self.id, &mut device_id, &mut subsystem_id))?; + } + Ok((device_id, subsystem_id)) + } + + /// Retrieve the static frame rate limit value of the vGPU type. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotSupported`, if frame rate limiter is turned off for the vGPU type + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetFrameRateLimit")] + pub fn frame_rate_limit(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuTypeGetFrameRateLimit + .as_ref(), + )?; + + let mut limit = 0; + unsafe { + nvml_try(sym(self.id, &mut limit))?; + } + Ok(limit) + } + + /// Retrieve the vGPU framebuffer size in bytes. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetFramebufferSize")] + pub fn framebuffer_size(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuTypeGetFramebufferSize + .as_ref(), + )?; + + let mut size = 0; + unsafe { + nvml_try(sym(self.id, &mut size))?; + } + Ok(size) + } + + /// Retrieve the GPU Instance Profile ID for the vGPU type. The API will return a valid GPU + /// Instance Profile ID for the MIG capable vGPU types, else + /// [`crate::ffi::bindings::INVALID_GPU_INSTANCE_PROFILE_ID`] is returned. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetGpuInstanceProfileId")] + pub fn instance_profile_id(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuTypeGetGpuInstanceProfileId + .as_ref(), + )?; + + let mut profile_id = 0; + unsafe { + nvml_try(sym(self.id, &mut profile_id))?; + } + Ok(profile_id) + } + + /// Retrieve the maximum number of vGPU instances creatable on a device for the vGPU type. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetMaxInstances")] + pub fn max_instances(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetMaxInstances.as_ref())?; + + let mut max = 0; + unsafe { + nvml_try(sym(self.device.handle(), self.id, &mut max))?; + } + Ok(max) + } + + /// Retrieve the maximum number of vGPU instances supported per VM for the vGPU type. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetMaxInstancesPerVm")] + pub fn max_instances_per_vm(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuTypeGetMaxInstancesPerVm + .as_ref(), + )?; + + let mut max = 0; + unsafe { + nvml_try(sym(self.id, &mut max))?; + } + Ok(max) + } + + /// Retrieve count of vGPU's supported display heads. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetNumDisplayHeads")] + pub fn num_display_heads(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuTypeGetNumDisplayHeads + .as_ref(), + )?; + + let mut heads = 0; + unsafe { + nvml_try(sym(self.id, &mut heads))?; + } + Ok(heads) + } + + /// Retrieve vGPU display head's maximum supported resolution. + /// + /// The `display_head` argument specifies the 0-based display index, the + /// maximum being what [`VgpuType::num_display_heads`] returns. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg`, if this `Device` is invalid + /// * `Unknown`, on any unexpected error + /// + /// # Device Support + /// + /// Kepler or newer fully supported devices. + #[doc(alias = "nvmlVgpuTypeGetResolution")] + pub fn resolution(&self, display_head: u32) -> Result<(u32, u32), NvmlError> { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetResolution.as_ref())?; + + let (mut x, mut y) = (0, 0); + unsafe { + nvml_try(sym(self.id, display_head, &mut x, &mut y))?; + } + Ok((x, y)) + } +}