From 07769dfd9b78543056b7c4420065d42a3d1ab8f0 Mon Sep 17 00:00:00 2001 From: Robert Masen Date: Thu, 5 Mar 2026 19:27:02 +0000 Subject: [PATCH 1/4] Increase vGPU API coverage --- nvml-wrapper/src/device.rs | 25 +- nvml-wrapper/src/enum_wrappers/mod.rs | 1 + nvml-wrapper/src/enum_wrappers/vgpu.rs | 47 ++ nvml-wrapper/src/struct_wrappers/mod.rs | 1 + nvml-wrapper/src/struct_wrappers/vgpu.rs | 130 ++++++ nvml-wrapper/src/test_utils.rs | 7 +- nvml-wrapper/src/vgpu.rs | 533 ++++++++++++++++++++++- 7 files changed, 733 insertions(+), 11 deletions(-) create mode 100644 nvml-wrapper/src/enum_wrappers/vgpu.rs create mode 100644 nvml-wrapper/src/struct_wrappers/vgpu.rs diff --git a/nvml-wrapper/src/device.rs b/nvml-wrapper/src/device.rs index d7447c1..d7702e5 100644 --- a/nvml-wrapper/src/device.rs +++ b/nvml-wrapper/src/device.rs @@ -9,6 +9,8 @@ use crate::bitmasks::device::ThrottleReasons; use crate::bitmasks::event::EventTypes; #[cfg(target_os = "windows")] use crate::bitmasks::Behavior; +#[cfg(target_os = "linux")] +use crate::vgpu::VgpuInstance; use crate::enum_wrappers::{bool_from_state, device::*, state_from_bool}; @@ -6174,18 +6176,25 @@ impl<'nvml> Device<'nvml> { // Tested #[cfg(target_os = "linux")] #[doc(alias = "nvmlDeviceGetActiveVgpus")] - pub fn active_vgpus(&self) -> Result, NvmlError> { + pub fn active_vgpus<'a>(&'a self) -> Result>, NvmlError> + where + 'a: 'nvml, + { let sym = nvml_sym(self.nvml.lib.nvmlDeviceGetActiveVgpus.as_ref())?; - unsafe { + let raw_vgpus = unsafe { let mut count: u32 = 0; nvml_try_count(sym(self.device, &mut count, std::ptr::null_mut()))?; let mut arr: Vec = vec![0; count as usize]; nvml_try(sym(self.device, &mut count, arr.as_mut_ptr()))?; - Ok(arr) - } + arr + }; + Ok(raw_vgpus + .into_iter() + .map(|raw| VgpuInstance::new(raw, self)) + .collect()) } /** @@ -7950,7 +7959,13 @@ mod test { #[test] fn active_vgpus() { let nvml = nvml(); - test_with_device(3, &nvml, |device| device.active_vgpus()) + test_with_device(3, &nvml, |device| { + Ok(device + .active_vgpus()? + .into_iter() + .map(|v| v.instance) + .collect::>()) + }) } #[test] diff --git a/nvml-wrapper/src/enum_wrappers/mod.rs b/nvml-wrapper/src/enum_wrappers/mod.rs index 2093e98..be08497 100644 --- a/nvml-wrapper/src/enum_wrappers/mod.rs +++ b/nvml-wrapper/src/enum_wrappers/mod.rs @@ -4,6 +4,7 @@ use crate::ffi::bindings::*; pub mod device; pub mod nv_link; pub mod unit; +pub mod vgpu; pub fn bool_from_state(state: nvmlEnableState_t) -> Result { match state { diff --git a/nvml-wrapper/src/enum_wrappers/vgpu.rs b/nvml-wrapper/src/enum_wrappers/vgpu.rs new file mode 100644 index 0000000..ef6398b --- /dev/null +++ b/nvml-wrapper/src/enum_wrappers/vgpu.rs @@ -0,0 +1,47 @@ +use crate::error::NvmlError; +use ffi::bindings::*; +#[cfg(feature = "serde")] +use serde_derive::{Deserialize, Serialize}; +use wrapcenum_derive::EnumWrapper; + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum VmId { + Domain(String), + Uuid(String), +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(u32)] +pub enum VgpuLicenseState { + Unknown = NVML_GRID_LICENSE_STATE_UNKNOWN, + Uninitialized = NVML_GRID_LICENSE_STATE_UNINITIALIZED, + UnlicensedUnrestricted = NVML_GRID_LICENSE_STATE_UNLICENSED_UNRESTRICTED, + UnlicensedRestricted = NVML_GRID_LICENSE_STATE_UNLICENSED_RESTRICTED, + Unlicensed = NVML_GRID_LICENSE_STATE_UNLICENSED, + Licensed = NVML_GRID_LICENSE_STATE_LICENSED, +} + +impl From for VgpuLicenseState { + fn from(value: u32) -> Self { + match value { + NVML_GRID_LICENSE_STATE_UNINITIALIZED => Self::Uninitialized, + NVML_GRID_LICENSE_STATE_UNLICENSED_UNRESTRICTED => Self::UnlicensedUnrestricted, + NVML_GRID_LICENSE_STATE_UNLICENSED_RESTRICTED => Self::UnlicensedRestricted, + NVML_GRID_LICENSE_STATE_UNLICENSED => Self::Unlicensed, + NVML_GRID_LICENSE_STATE_LICENSED => Self::Licensed, + _ => Self::Unknown, + } + } +} + +//nvmlVgpuGuestInfoState_enum +#[derive(EnumWrapper, Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[wrap(c_enum = "nvmlVgpuGuestInfoState_enum")] +pub enum VgpuGuestInfoState { + #[wrap(c_variant = "NVML_VGPU_INSTANCE_GUEST_INFO_STATE_UNINITIALIZED")] + Uninitialized, + #[wrap(c_variant = "NVML_VGPU_INSTANCE_GUEST_INFO_STATE_INITIALIZED")] + Initialized, +} diff --git a/nvml-wrapper/src/struct_wrappers/mod.rs b/nvml-wrapper/src/struct_wrappers/mod.rs index b0f0436..d3940a9 100644 --- a/nvml-wrapper/src/struct_wrappers/mod.rs +++ b/nvml-wrapper/src/struct_wrappers/mod.rs @@ -3,6 +3,7 @@ pub mod event; pub mod gpm; pub mod nv_link; pub mod unit; +pub mod vgpu; use self::device::PciInfo; use crate::error::NvmlError; diff --git a/nvml-wrapper/src/struct_wrappers/vgpu.rs b/nvml-wrapper/src/struct_wrappers/vgpu.rs new file mode 100644 index 0000000..d820569 --- /dev/null +++ b/nvml-wrapper/src/struct_wrappers/vgpu.rs @@ -0,0 +1,130 @@ +use std::os::raw::c_char; + +use ffi::bindings::*; + +use crate::{ + enum_wrappers::vgpu::{VgpuGuestInfoState, VgpuLicenseState}, + error::NvmlError, +}; + +pub struct VgpuLicenseInfo { + pub is_licensed: bool, + pub expiry: VgpuLicenseExpiry, + pub state: VgpuLicenseState, +} + +impl From for VgpuLicenseInfo { + fn from(value: nvmlVgpuLicenseInfo_t) -> Self { + Self { + is_licensed: value.isLicensed != 0, + expiry: VgpuLicenseExpiry::from(value.licenseExpiry), + state: VgpuLicenseState::from(value.currentState), + } + } +} + +pub struct VgpuLicenseExpiry { + pub year: u16, + pub month: u8, + pub day: u8, + pub hour: u8, + pub min: u8, + pub sec: u8, + pub status: u8, +} + +impl From for VgpuLicenseExpiry { + fn from(value: nvmlVgpuLicenseExpiry_t) -> Self { + Self { + year: u16::try_from(value.year).unwrap_or(u16::MAX), + month: u8::try_from(value.month).unwrap_or(u8::MAX), + day: u8::try_from(value.day).unwrap_or(u8::MAX), + hour: u8::try_from(value.hour).unwrap_or(u8::MAX), + min: u8::try_from(value.min).unwrap_or(u8::MAX), + sec: u8::try_from(value.sec).unwrap_or(u8::MAX), + status: value.status, + } + } +} + +pub struct VgpuMetadata { + pub version: u32, + pub revision: u32, + pub guest_info_state: VgpuGuestInfoState, + pub guest_driver_version: String, + pub host_driver_version: String, + pub vgpu_virtualization_caps: u32, + pub guest_vgpu_version: u32, +} + +impl TryFrom for VgpuMetadata { + type Error = NvmlError; + fn try_from(value: nvmlVgpuMetadata_t) -> Result { + let convert_c_str = |c_str: &[c_char]| { + let mut ret = String::with_capacity(c_str.len()); + for &byte in c_str { + if byte == 0 { + break; + } + let us = u8::try_from(byte).map_err(|_| NvmlError::Unknown)?; + ret.push(us as char); + } + + Ok::(ret) + }; + + Ok(Self { + version: value.version, + revision: value.revision, + guest_driver_version: convert_c_str(&value.guestDriverVersion)?, + host_driver_version: convert_c_str(&value.hostDriverVersion)?, + vgpu_virtualization_caps: value.vgpuVirtualizationCaps, + guest_vgpu_version: value.guestVgpuVersion, + guest_info_state: value.guestInfoState.try_into()?, + }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct VgpuPlacementId { + pub version: u32, + pub id: u32, +} + +impl From for VgpuPlacementId { + fn from(value: nvmlVgpuPlacementId_t) -> Self { + Self { + version: value.version, + id: value.placementId, + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct VgpuRuntimeState { + pub version: u32, + pub size: u64, +} + +impl From for VgpuRuntimeState { + fn from(value: nvmlVgpuRuntimeState_t) -> Self { + Self { + version: value.version, + size: value.size, + } + } +} + +pub struct Bar1Info { + pub version: u32, + pub size: u64, +} + +impl From for Bar1Info { + fn from(value: nvmlVgpuTypeBar1Info_v1_t) -> Self { + Self { + version: value.version, + size: value.bar1Size, + } + } +} diff --git a/nvml-wrapper/src/test_utils.rs b/nvml-wrapper/src/test_utils.rs index 4482551..a151016 100644 --- a/nvml-wrapper/src/test_utils.rs +++ b/nvml-wrapper/src/test_utils.rs @@ -15,6 +15,7 @@ use crate::enums::unit::*; use crate::error::NvmlError; use crate::event::EventSet; use crate::struct_wrappers::gpm::GpmMetricResult; +use crate::vgpu::VgpuInstance; use std::fmt::Debug; use crate::struct_wrappers::nv_link::*; @@ -124,6 +125,8 @@ impl ShouldPrint for Vec {} impl ShouldPrint for (VgpuVersion, VgpuVersion) {} impl ShouldPrint for ProfileInfo {} impl ShouldPrint for GspFirmwareMode {} +impl<'dev> ShouldPrint for VgpuInstance<'dev> {} +impl<'dev> ShouldPrint for Vec> {} #[cfg(target_os = "windows")] impl ShouldPrint for DriverModelState {} @@ -151,9 +154,9 @@ where multi(reps, test); } -pub fn test_with_device(reps: usize, nvml: &Nvml, test: T) +pub fn test_with_device<'a, T, R>(reps: usize, nvml: &'a Nvml, test: T) where - T: Fn(&Device) -> Result, + T: Fn(&Device<'a>) -> Result, R: ShouldPrint, { let device = device(nvml); diff --git a/nvml-wrapper/src/vgpu.rs b/nvml-wrapper/src/vgpu.rs index 25d1624..5a336c0 100644 --- a/nvml-wrapper/src/vgpu.rs +++ b/nvml-wrapper/src/vgpu.rs @@ -1,13 +1,27 @@ -use std::{ffi::CStr, os::raw::c_uint}; +use std::{ + ffi::CStr, + os::raw::{c_char, c_uint}, +}; use ffi::bindings::{ - nvmlVgpuCapability_t, nvmlVgpuTypeId_t, NVML_DEVICE_NAME_BUFFER_SIZE, - NVML_GRID_LICENSE_BUFFER_SIZE, + nvmlEnableState_enum_NVML_FEATURE_ENABLED, nvmlEncoderSessionInfo_t, nvmlFBCSessionInfo_t, + nvmlFBCStats_t, nvmlVgpuCapability_t, nvmlVgpuInstance_t, nvmlVgpuLicenseInfo_st, + nvmlVgpuMetadata_t, nvmlVgpuPlacementId_t, nvmlVgpuRuntimeState_t, nvmlVgpuTypeBar1Info_v1_t, + nvmlVgpuTypeId_t, nvmlVgpuVmIdType_NVML_VGPU_VM_ID_DOMAIN_ID, + nvmlVgpuVmIdType_NVML_VGPU_VM_ID_UUID, NVML_DEVICE_NAME_BUFFER_SIZE, + NVML_DEVICE_UUID_BUFFER_SIZE, NVML_GRID_LICENSE_BUFFER_SIZE, + NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE, }; use static_assertions::assert_impl_all; use crate::{ - error::{nvml_sym, nvml_try, NvmlError}, + enum_wrappers::vgpu::VmId, + error::{nvml_sym, nvml_try, nvml_try_count, NvmlError}, + struct_wrappers::{ + device::{EncoderSessionInfo, FbcSessionInfo, FbcStats}, + vgpu::{Bar1Info, VgpuLicenseInfo, VgpuMetadata, VgpuPlacementId, VgpuRuntimeState}, + }, + structs::device::EncoderStats, Device, }; @@ -359,4 +373,515 @@ impl<'dev> VgpuType<'dev> { } Ok((x, y)) } + + #[doc(alias = "nvmlVgpuTypeGetBAR1Info")] + pub fn get_bar1_info(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetBAR1Info.as_ref())?; + let mut info: nvmlVgpuTypeBar1Info_v1_t; + unsafe { + info = std::mem::zeroed(); + nvml_try(sym(self.id, &mut info))?; + } + Ok(info.into()) + } + + #[doc(alias = "nvmlVgpuTypeGetFbReservation")] + pub fn get_fb_reservation(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetFbReservation.as_ref())?; + let mut res = 0; + unsafe { + nvml_try(sym(self.id, &mut res))?; + } + Ok(res) + } + + #[doc(alias = "nvmlVgpuTypeGetGspHeapSize")] + pub fn get_gsp_heap_size(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetGspHeapSize.as_ref())?; + let mut res = 0; + unsafe { + nvml_try(sym(self.id, &mut res))?; + } + Ok(res) + } +} + +pub struct VgpuInstance<'dev> { + pub(crate) instance: nvmlVgpuInstance_t, + device: &'dev Device<'dev>, +} + +assert_impl_all!(VgpuInstance: Send, Sync); + +impl<'dev> VgpuInstance<'dev> { + pub(crate) fn new(instance: nvmlVgpuInstance_t, device: &'dev Device<'dev>) -> Self { + Self { instance, device } + } + + /** + Retrieve the VM ID associated with a vGPU instance. + + The VM ID is returned as a string, not exceeding 80 characters in length (including the NUL + terminator). See nvmlConstants::NVML_DEVICE_UUID_BUFFER_SIZE. + + The format of the VM ID varies by platform, and is indicated by the type identifier returned + in vmIdType. + + # Errors + + * `Uninitialized` if the library has not been successfully initialized + * `NotFound` if self does not match a valid active vGPU instance on the system + * `Unknown` on any unexpected error + + # Platform Support + + For Kepler or newer fully supported devices. + */ + #[doc(alias = "nvmlVgpuInstanceGetVmID")] + pub fn get_vm_id(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetVmID.as_ref())?; + let mut s = [0; NVML_DEVICE_UUID_BUFFER_SIZE as usize]; + let mut id_type = 0; + let id = unsafe { + nvml_try(sym( + self.instance, + s.as_mut_ptr(), + NVML_DEVICE_UUID_BUFFER_SIZE, + &mut id_type, + ))?; + CStr::from_ptr(s.as_ptr()) + }; + + let id = id.to_str()?.to_string(); + Ok(match id_type { + nvmlVgpuVmIdType_NVML_VGPU_VM_ID_DOMAIN_ID => VmId::Domain(id), + nvmlVgpuVmIdType_NVML_VGPU_VM_ID_UUID => VmId::Uuid(id), + _ => return Err(NvmlError::Unknown), + }) + } + + /** + Retrieve the framebuffer usage in bytes. + + Framebuffer usage is the amont of vGPU framebuffer memory that is currently in use by the VM + + # Errors + + * `Uninitialized`, if the library has not been successfully initialized + * `InvalidArg` if self is invalid + * `NotFound` if self does not match a valid active vGPU instance on the system + * `Unknown`, on any unexpected error + + # Platform Support + + For Kepler or newer fully supported devices. + */ + #[doc(alias = "nvmlVgpuInstanceGetFbUsage")] + pub fn get_fb_usage(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetFbUsage.as_ref())?; + let mut usage = 0; + unsafe { + nvml_try(sym(self.instance, &mut usage))?; + } + Ok(usage) + } + + /** + Retrieve the vGPU type of a vGPU instance + + Returns the vGPU type ID of vgpu assigned to the vGPU instance. + + # Errors + + * `Uninitialized`, if the library has not been successfully initialized + * `InvalidArg` if self is invalid + * `NotFound` if self does not match a valid active vGPU instance on the system + * `Unknown`, on any unexpected error + + # Platform Support + + For Maxwell or newer fully supported devices + */ + #[doc(alias = "nvmlVgpuInstanceGetType")] + pub fn get_instance_type(&'dev self) -> Result, NvmlError> { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetType.as_ref())?; + let mut raw_type = 0; + unsafe { + nvml_try(sym(self.instance, &mut raw_type))?; + } + Ok(VgpuType::new(self.device, raw_type)) + } + + /** + Get the list of process ids running on this vGPU instance for stats purpose + + see [`crate::device::Device::vgpu_accounting_pids`] for details + */ + #[doc(alias = "nvmlVgpuInstanceGetAccountingPids")] + pub fn accounting_pids(&self) -> Result, NvmlError> { + self.device.vgpu_accounting_pids(self.instance) + } + + #[doc(alias = "nvmlVgpuInstanceClearAccountingPids")] + pub fn clear_accounting_pids(&self) -> Result<(), NvmlError> { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceClearAccountingPids + .as_ref(), + )?; + unsafe { nvml_try(sym(self.instance)) } + } + + #[doc(alias = "nvmlVgpuInstanceGetAccountingMode")] + pub fn get_accounting_mode(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetAccountingMode + .as_ref(), + )?; + let mut mode = 0; + unsafe { + nvml_try(sym(self.instance, &mut mode))?; + } + Ok(mode == nvmlEnableState_enum_NVML_FEATURE_ENABLED) + } + + #[doc(alias = "nvmlVgpuInstanceGetEccMode")] + pub fn get_ecc_mode(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetEccMode.as_ref())?; + let mut mode = 0; + unsafe { + nvml_try(sym(self.instance, &mut mode))?; + } + Ok(mode == nvmlEnableState_enum_NVML_FEATURE_ENABLED) + } + + #[doc(alias = "nvmlVgpuInstanceGetEncoderCapacity")] + pub fn get_encoder_capacity(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetEncoderCapacity + .as_ref(), + )?; + let mut cap = 0; + unsafe { + nvml_try(sym(self.instance, &mut cap))?; + } + Ok(cap) + } + + #[doc(alias = "nvmlVgpuInstanceGetEncoderSessions")] + pub fn get_encoder_sessions(&self) -> Result, NvmlError> { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetEncoderSessions + .as_ref(), + )?; + let mut count = self.get_encoder_session_count()?; + let mut raw_sessions: Vec; + unsafe { + raw_sessions = vec![std::mem::zeroed(); count as usize]; + nvml_try(sym(self.instance, &mut count, raw_sessions.as_mut_ptr()))?; + }; + raw_sessions + .into_iter() + .map(EncoderSessionInfo::try_from) + .collect() + } + + #[doc(alias = "nvmlVgpuInstanceGetEncoderStats")] + pub fn get_encoder_stats(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetEncoderStats + .as_ref(), + )?; + let mut session_count = self.get_encoder_session_count()?; + let mut average_fps = 0; + let mut average_latency = 0; + unsafe { + nvml_try(sym( + self.instance, + &mut session_count, + &mut average_fps, + &mut average_latency, + ))?; + }; + Ok(EncoderStats { + session_count, + average_fps, + average_latency, + }) + } + + #[doc(alias = "nvmlVgpuInstanceGetFBCSessions")] + pub fn get_fbc_sessions(&self) -> Result, NvmlError> { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetFBCSessions + .as_ref(), + )?; + let mut session_count = 0; + let mut info: Vec; + unsafe { + nvml_try_count(sym(self.instance, &mut session_count, std::ptr::null_mut()))?; + if session_count == 0 { + return Ok(Vec::new()); + } + info = vec![std::mem::zeroed(); session_count as usize]; + nvml_try(sym(self.instance, &mut session_count, info.as_mut_ptr()))?; + }; + info.into_iter().map(FbcSessionInfo::try_from).collect() + } + + #[doc(alias = "nvmlVgpuInstanceGetFBCStats")] + pub fn get_fbc_stats(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetFBCStats.as_ref())?; + unsafe { + let mut info: nvmlFBCStats_t = std::mem::zeroed(); + nvml_try(sym(self.instance, &mut info))?; + Ok(FbcStats::from(info)) + } + } + + #[doc(alias = "nvmlVgpuInstanceGetFrameRateLimit")] + pub fn get_frame_rate_limit(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetFrameRateLimit + .as_ref(), + )?; + let mut limit = 0; + unsafe { + nvml_try(sym(self.instance, &mut limit))?; + }; + Ok(limit) + } + + #[doc(alias = "nvmlVgpuInstanceGetGpuInstanceId")] + pub fn get_gpu_instance_id(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetGpuInstanceId + .as_ref(), + )?; + let mut id = 0; + unsafe { + nvml_try(sym(self.instance, &mut id))?; + }; + Ok(id) + } + + #[doc(alias = "nvmlVgpuInstanceGetGpuPciId")] + pub fn get_gpu_pci_id(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetGpuPciId.as_ref())?; + let mut buffer: Vec; + let mut count = 0; + let raw_id = unsafe { + nvml_try_count(sym(self.instance, [0; 1].as_mut_ptr(), &mut count))?; + if count == 0 { + return Ok(String::new()); + } + buffer = vec![0; count as usize]; + nvml_try(sym(self.instance, buffer.as_mut_ptr(), &mut count))?; + CStr::from_ptr(buffer.as_ptr()) + }; + Ok(raw_id.to_str()?.to_string()) + } + + #[cfg(feature = "legacy-functions")] + #[doc(alias = "nvmlVgpuInstanceGetLicenseInfo")] + pub fn get_license_info(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetLicenseInfo + .as_ref(), + )?; + let mut info: nvmlVgpuLicenseInfo_st; + + unsafe { + info = std::mem::zeroed(); + nvml_try(sym(self.instance, &mut info))?; + }; + Ok(VgpuLicenseInfo::from(info)) + } + + #[doc(alias = "nvmlVgpuInstanceGetLicenseInfo_v2")] + pub fn get_license_info_v2(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetLicenseInfo_v2 + .as_ref(), + )?; + let mut info: nvmlVgpuLicenseInfo_st; + + unsafe { + info = std::mem::zeroed(); + nvml_try(sym(self.instance, &mut info))?; + }; + Ok(VgpuLicenseInfo::from(info)) + } + + #[doc(alias = "nvmlVgpuInstanceGetMdevUUID")] + pub fn get_mdev_uuid(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetMdevUUID.as_ref())?; + let mut buffer: [c_char; NVML_DEVICE_UUID_BUFFER_SIZE as usize] = + [0; NVML_DEVICE_UUID_BUFFER_SIZE as usize]; + + unsafe { + nvml_try(sym( + self.instance, + buffer.as_mut_ptr(), + NVML_DEVICE_UUID_BUFFER_SIZE, + ))?; + let raw_id = CStr::from_ptr(buffer.as_ptr()); + Ok(raw_id.to_str()?.to_string()) + } + } + + #[doc(alias = "nvmlVgpuInstanceGetMetadata")] + pub fn get_metadata(&self) -> Result, NvmlError> { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetMetadata.as_ref())?; + let mut metadata: Vec; + let mut count = 0; + unsafe { + nvml_try_count(sym(self.instance, std::ptr::null_mut(), &mut count))?; + metadata = vec![std::mem::zeroed(); count as usize]; + nvml_try(sym(self.instance, metadata.as_mut_ptr(), &mut count))?; + } + metadata.into_iter().map(VgpuMetadata::try_from).collect() + } + + #[doc(alias = "nvmlVgpuInstanceGetPlacementId")] + pub fn get_get_placement_id(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetPlacementId + .as_ref(), + )?; + let mut raw_placement_id: nvmlVgpuPlacementId_t; + unsafe { + raw_placement_id = std::mem::zeroed(); + nvml_try(sym(self.instance, &mut raw_placement_id))?; + } + Ok(raw_placement_id.into()) + } + + #[doc(alias = "nvmlVgpuInstanceGetRuntimeStateSize")] + pub fn get_get_runtime_state_size(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetRuntimeStateSize + .as_ref(), + )?; + let mut raw_state: nvmlVgpuRuntimeState_t; + unsafe { + raw_state = std::mem::zeroed(); + nvml_try(sym(self.instance, &mut raw_state))?; + } + Ok(raw_state.into()) + } + + #[doc(alias = "nvmlVgpuInstanceGetUUID")] + pub fn get_uuid(&self) -> Result { + let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetUUID.as_ref())?; + let mut buffer: [c_char; NVML_DEVICE_UUID_BUFFER_SIZE as usize] = + [0; NVML_DEVICE_UUID_BUFFER_SIZE as usize]; + + unsafe { + nvml_try(sym( + self.instance, + buffer.as_mut_ptr(), + NVML_DEVICE_UUID_BUFFER_SIZE, + ))?; + let raw_id = CStr::from_ptr(buffer.as_ptr()); + Ok(raw_id.to_str()?.to_string()) + } + } + + #[doc(alias = "nvmlVgpuInstanceGetVmDriverVersion")] + pub fn get_driver_version(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetVmDriverVersion + .as_ref(), + )?; + let mut buffer: [c_char; NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE as usize] = + [0; NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE as usize]; + + unsafe { + nvml_try(sym( + self.instance, + buffer.as_mut_ptr(), + NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE, + ))?; + let raw_id = CStr::from_ptr(buffer.as_ptr()); + Ok(raw_id.to_str()?.to_string()) + } + } + + #[doc(alias = "nvmlVgpuInstanceSetEncoderCapacity")] + pub fn set_encoder_capacity(&self, capacity: u32) -> Result<(), NvmlError> { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceSetEncoderCapacity + .as_ref(), + )?; + + unsafe { + nvml_try(sym(self.instance, capacity))?; + } + Ok(()) + } + + fn get_encoder_session_count(&self) -> Result { + let sym = nvml_sym( + self.device + .nvml() + .lib + .nvmlVgpuInstanceGetEncoderSessions + .as_ref(), + )?; + let mut count = 0; + unsafe { + nvml_try_count(sym(self.instance, &mut count, std::ptr::null_mut()))?; + }; + Ok(count) + } +} + +impl<'dev> std::fmt::Debug for VgpuInstance<'dev> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VgpuInstance") + .field("instance", &self.instance) + .finish_non_exhaustive() + } } From 970027be42d388d3dfafb0c008a0d185fb4ca32b Mon Sep 17 00:00:00 2001 From: Robert Masen Date: Thu, 5 Mar 2026 19:27:11 +0000 Subject: [PATCH 2/4] update unwrapped_functions.txt --- nvml-wrapper/unwrapped_functions.txt | 83 +--------------------------- 1 file changed, 3 insertions(+), 80 deletions(-) diff --git a/nvml-wrapper/unwrapped_functions.txt b/nvml-wrapper/unwrapped_functions.txt index ff127c8..2e3635b 100644 --- a/nvml-wrapper/unwrapped_functions.txt +++ b/nvml-wrapper/unwrapped_functions.txt @@ -5,7 +5,6 @@ nvmlComputeInstanceGetInfo_v2 nvmlDeviceClearFieldValues nvmlDeviceCreateGpuInstance nvmlDeviceCreateGpuInstanceWithPlacement -nvmlDeviceGetActiveVgpus nvmlDeviceGetAdaptiveClockInfoStatus nvmlDeviceGetC2cModeInfoV nvmlDeviceGetCapabilities @@ -14,11 +13,9 @@ nvmlDeviceGetComputeInstanceId nvmlDeviceGetConfComputeMemSizeInfo nvmlDeviceGetConfComputeProtectedMemoryUsage nvmlDeviceGetCoolerInfo -nvmlDeviceGetCreatableVgpus nvmlDeviceGetCurrentClockFreqs nvmlDeviceGetCurrentClocksEventReasons nvmlDeviceGetDefaultEccMode -nvmlDeviceGetDeviceHandleFromMigDeviceHandle nvmlDeviceGetDramEncryptionMode nvmlDeviceGetDynamicPstatesInfo nvmlDeviceGetGpcClkMinMaxVfOffset @@ -26,9 +23,6 @@ nvmlDeviceGetGpuFabricInfo nvmlDeviceGetGpuFabricInfoV nvmlDeviceGetGpuInstanceById nvmlDeviceGetGpuInstanceId -nvmlDeviceGetGpuInstancePossiblePlacements -nvmlDeviceGetGpuInstancePossiblePlacements_v2 -nvmlDeviceGetGpuInstanceProfileInfo nvmlDeviceGetGpuInstanceProfileInfoV nvmlDeviceGetGpuInstanceRemainingCapacity nvmlDeviceGetGpuInstances @@ -37,28 +31,15 @@ nvmlDeviceGetGridLicensableFeatures nvmlDeviceGetGridLicensableFeatures_v2 nvmlDeviceGetGridLicensableFeatures_v3 nvmlDeviceGetGridLicensableFeatures_v4 -nvmlDeviceGetGspFirmwareMode -nvmlDeviceGetGspFirmwareVersion -nvmlDeviceGetHostVgpuMode nvmlDeviceGetJpgUtilization nvmlDeviceGetLastBBXFlushTime nvmlDeviceGetMarginTemperature -nvmlDeviceGetMaxMigDeviceCount nvmlDeviceGetMemClkMinMaxVfOffset -nvmlDeviceGetMemoryAffinity -nvmlDeviceGetMigDeviceHandleByIndex -nvmlDeviceGetMigMode nvmlDeviceGetModuleId -nvmlDeviceGetMPSComputeRunningProcesses -nvmlDeviceGetMPSComputeRunningProcesses_v2 -nvmlDeviceGetMPSComputeRunningProcesses_v3 -nvmlDeviceGetNumaNodeId nvmlDeviceGetNvlinkBwMode -nvmlDeviceGetNvLinkRemoteDeviceType nvmlDeviceGetNvlinkSupportedBwModes nvmlDeviceGetOfaUtilization nvmlDeviceGetPciInfoExt -nvmlDeviceGetPerformanceModes nvmlDeviceGetPgpuMetadataString nvmlDeviceGetPlatformInfo nvmlDeviceGetProcessesUtilizationInfo @@ -67,36 +48,25 @@ nvmlDeviceGetRowRemapperHistogram nvmlDeviceGetRunningProcessDetailList nvmlDeviceGetSramEccErrorStatus nvmlDeviceGetSupportedClocksEventReasons -nvmlDeviceGetSupportedVgpus nvmlDeviceGetTargetFanSpeed nvmlDeviceGetTemperatureV nvmlDeviceGetThermalSettings -nvmlDeviceGetVgpuCapabilities nvmlDeviceGetVgpuHeterogeneousMode nvmlDeviceGetVgpuInstancesUtilizationInfo nvmlDeviceGetVgpuMetadata nvmlDeviceGetVgpuProcessesUtilizationInfo nvmlDeviceGetVgpuProcessUtilization -nvmlDeviceGetVgpuSchedulerCapabilities -nvmlDeviceGetVgpuSchedulerLog -nvmlDeviceGetVgpuSchedulerState nvmlDeviceGetVgpuTypeCreatablePlacements nvmlDeviceGetVgpuTypeSupportedPlacements nvmlDeviceGetVgpuUtilization -nvmlDeviceGetVirtualizationMode -nvmlDeviceIsMigDeviceHandle nvmlDevicePowerSmoothingActivatePresetProfile nvmlDevicePowerSmoothingSetState nvmlDevicePowerSmoothingUpdatePresetProfileParam nvmlDeviceSetConfComputeUnprotectedMemSize nvmlDeviceSetDramEncryptionMode -nvmlDeviceSetMigMode nvmlDeviceSetNvlinkBwMode nvmlDeviceSetNvLinkDeviceLowPowerThreshold -nvmlDeviceSetTemperatureThreshold -nvmlDeviceSetVgpuCapabilities nvmlDeviceSetVgpuHeterogeneousMode -nvmlDeviceSetVgpuSchedulerState nvmlDeviceSetVirtualizationMode nvmlDeviceWorkloadPowerProfileClearRequestedProfiles nvmlDeviceWorkloadPowerProfileGetCurrentProfiles @@ -104,8 +74,6 @@ nvmlDeviceWorkloadPowerProfileGetProfilesInfo nvmlDeviceWorkloadPowerProfileSetRequestedProfiles nvmlErrorString nvmlGetVgpuCompatibility -nvmlGetVgpuDriverCapabilities -nvmlGetVgpuVersion nvmlGpuInstanceCreateComputeInstance nvmlGpuInstanceCreateComputeInstanceWithPlacement nvmlGpuInstanceDestroy @@ -116,58 +84,10 @@ nvmlGpuInstanceGetComputeInstanceProfileInfoV nvmlGpuInstanceGetComputeInstanceRemainingCapacity nvmlGpuInstanceGetComputeInstances nvmlGpuInstanceGetInfo -nvmlSetVgpuVersion -nvmlSystemGetConfComputeCapabilities -nvmlSystemGetConfComputeGpusReadyState nvmlSystemGetConfComputeKeyRotationThresholdInfo -nvmlSystemGetConfComputeSettings -nvmlSystemGetConfComputeState nvmlSystemGetDriverBranch -nvmlSystemGetNvlinkBwMode -nvmlSystemSetConfComputeGpusReadyState nvmlSystemSetConfComputeKeyRotationThresholdInfo -nvmlSystemSetNvlinkBwMode -nvmlVgpuInstanceClearAccountingPids -nvmlVgpuInstanceGetAccountingMode -nvmlVgpuInstanceGetAccountingPids -nvmlVgpuInstanceGetAccountingStats -nvmlVgpuInstanceGetEccMode -nvmlVgpuInstanceGetEncoderCapacity -nvmlVgpuInstanceGetEncoderSessions -nvmlVgpuInstanceGetEncoderStats -nvmlVgpuInstanceGetFBCSessions -nvmlVgpuInstanceGetFBCStats -nvmlVgpuInstanceGetFbUsage -nvmlVgpuInstanceGetFrameRateLimit -nvmlVgpuInstanceGetGpuInstanceId -nvmlVgpuInstanceGetGpuPciId -nvmlVgpuInstanceGetLicenseInfo -nvmlVgpuInstanceGetLicenseInfo_v2 nvmlVgpuInstanceGetLicenseStatus -nvmlVgpuInstanceGetMdevUUID -nvmlVgpuInstanceGetMetadata -nvmlVgpuInstanceGetPlacementId -nvmlVgpuInstanceGetRuntimeStateSize -nvmlVgpuInstanceGetType -nvmlVgpuInstanceGetUUID -nvmlVgpuInstanceGetVmDriverVersion -nvmlVgpuInstanceGetVmID -nvmlVgpuInstanceSetEncoderCapacity -nvmlVgpuTypeGetBAR1Info -nvmlVgpuTypeGetCapabilities -nvmlVgpuTypeGetClass -nvmlVgpuTypeGetDeviceID -nvmlVgpuTypeGetFbReservation -nvmlVgpuTypeGetFramebufferSize -nvmlVgpuTypeGetFrameRateLimit -nvmlVgpuTypeGetGpuInstanceProfileId -nvmlVgpuTypeGetGspHeapSize -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. @@ -181,10 +101,13 @@ nvmlDeviceGetComputeRunningProcesses nvmlDeviceGetCount nvmlDeviceGetDriverModel_v2 nvmlDeviceGetFanSpeed +nvmlDeviceGetGpuInstancePossiblePlacements nvmlDeviceGetGraphicsRunningProcesses nvmlDeviceGetHandleByIndex nvmlDeviceGetHandleByPciBusId nvmlDeviceGetMemoryInfo +nvmlDeviceGetMPSComputeRunningProcesses +nvmlDeviceGetMPSComputeRunningProcesses_v2 nvmlDeviceGetNvLinkRemotePciInfo nvmlDeviceGetPciInfo nvmlDeviceGetPciInfo_v2 From 41c2aa2d0742167b9d3a505f312bf0e31409e0ba Mon Sep 17 00:00:00 2001 From: Robert Masen Date: Fri, 6 Mar 2026 17:40:54 +0000 Subject: [PATCH 3/4] add doc comments to vgpu additions --- nvml-wrapper/src/vgpu.rs | 357 ++++++++++++++++++++++++++++++++------- 1 file changed, 300 insertions(+), 57 deletions(-) diff --git a/nvml-wrapper/src/vgpu.rs b/nvml-wrapper/src/vgpu.rs index 5a336c0..f95117f 100644 --- a/nvml-wrapper/src/vgpu.rs +++ b/nvml-wrapper/src/vgpu.rs @@ -374,6 +374,15 @@ impl<'dev> VgpuType<'dev> { Ok((x, y)) } + /// Retrieve the BAR1 info for given vGPU type. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuTypeGetBAR1Info")] pub fn get_bar1_info(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetBAR1Info.as_ref())?; @@ -385,6 +394,12 @@ impl<'dev> VgpuType<'dev> { Ok(info.into()) } + /// Retrieve the static framebuffer reservation of the vGPU type in bytes + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// #[doc(alias = "nvmlVgpuTypeGetFbReservation")] pub fn get_fb_reservation(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetFbReservation.as_ref())?; @@ -395,6 +410,11 @@ impl<'dev> VgpuType<'dev> { Ok(res) } + /// Retrieve the static GSP heap size of the vGPU type in bytes + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized #[doc(alias = "nvmlVgpuTypeGetGspHeapSize")] pub fn get_gsp_heap_size(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuTypeGetGspHeapSize.as_ref())?; @@ -418,25 +438,23 @@ impl<'dev> VgpuInstance<'dev> { Self { instance, device } } - /** - Retrieve the VM ID associated with a vGPU instance. - - The VM ID is returned as a string, not exceeding 80 characters in length (including the NUL - terminator). See nvmlConstants::NVML_DEVICE_UUID_BUFFER_SIZE. - - The format of the VM ID varies by platform, and is indicated by the type identifier returned - in vmIdType. - - # Errors - - * `Uninitialized` if the library has not been successfully initialized - * `NotFound` if self does not match a valid active vGPU instance on the system - * `Unknown` on any unexpected error - - # Platform Support - - For Kepler or newer fully supported devices. - */ + /// Retrieve the VM ID associated with a vGPU instance. + /// + /// The VM ID is returned as a string, not exceeding 80 characters in length (including the NUL + /// terminator). See nvmlConstants::NVML_DEVICE_UUID_BUFFER_SIZE. + /// + /// The format of the VM ID varies by platform, and is indicated by the type identifier returned + /// in vmIdType. + /// + /// # Errors + /// + /// * `Uninitialized` if the library has not been successfully initialized + /// * `NotFound` if self does not match a valid active vGPU instance on the system + /// * `Unknown` on any unexpected error + /// + /// # Platform Support + /// + /// For Kepler or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetVmID")] pub fn get_vm_id(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetVmID.as_ref())?; @@ -460,22 +478,20 @@ impl<'dev> VgpuInstance<'dev> { }) } - /** - Retrieve the framebuffer usage in bytes. - - Framebuffer usage is the amont of vGPU framebuffer memory that is currently in use by the VM - - # Errors - - * `Uninitialized`, if the library has not been successfully initialized - * `InvalidArg` if self is invalid - * `NotFound` if self does not match a valid active vGPU instance on the system - * `Unknown`, on any unexpected error - - # Platform Support - - For Kepler or newer fully supported devices. - */ + /// Retrieve the framebuffer usage in bytes. + /// + /// Framebuffer usage is the amount of vGPU framebuffer memory that is currently in use by the VM + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg` if self is invalid + /// * `NotFound` if self does not match a valid active vGPU instance on the system + /// * `Unknown`, on any unexpected error + /// + /// # Platform Support + /// + /// For Kepler or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetFbUsage")] pub fn get_fb_usage(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetFbUsage.as_ref())?; @@ -486,22 +502,20 @@ impl<'dev> VgpuInstance<'dev> { Ok(usage) } - /** - Retrieve the vGPU type of a vGPU instance - - Returns the vGPU type ID of vgpu assigned to the vGPU instance. - - # Errors - - * `Uninitialized`, if the library has not been successfully initialized - * `InvalidArg` if self is invalid - * `NotFound` if self does not match a valid active vGPU instance on the system - * `Unknown`, on any unexpected error - - # Platform Support - - For Maxwell or newer fully supported devices - */ + /// Retrieve the vGPU type of a vGPU instance + /// + /// Returns the vGPU type ID of vgpu assigned to the vGPU instance. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `InvalidArg` if self is invalid + /// * `NotFound` if self does not match a valid active vGPU instance on the system + /// * `Unknown`, on any unexpected error + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices #[doc(alias = "nvmlVgpuInstanceGetType")] pub fn get_instance_type(&'dev self) -> Result, NvmlError> { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetType.as_ref())?; @@ -512,16 +526,25 @@ impl<'dev> VgpuInstance<'dev> { Ok(VgpuType::new(self.device, raw_type)) } - /** - Get the list of process ids running on this vGPU instance for stats purpose - - see [`crate::device::Device::vgpu_accounting_pids`] for details - */ + /// Get the list of process ids running on this vGPU instance for stats purpose + /// + /// see [`crate::device::Device::vgpu_accounting_pids`] for details #[doc(alias = "nvmlVgpuInstanceGetAccountingPids")] pub fn accounting_pids(&self) -> Result, NvmlError> { self.device.vgpu_accounting_pids(self.instance) } + /// Clears accounting information of the vGPU instance that have already terminated. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NoPermission`, if the user doesn't have permission to perform this operation + /// * `NotSupported`, if the vGPU doesn't support this feature or accounting mode is disabled + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. Requires root/admin permissions. #[doc(alias = "nvmlVgpuInstanceClearAccountingPids")] pub fn clear_accounting_pids(&self) -> Result<(), NvmlError> { let sym = nvml_sym( @@ -534,6 +557,18 @@ impl<'dev> VgpuInstance<'dev> { unsafe { nvml_try(sym(self.instance)) } } + /// Queries the state of per process accounting mode on vGPU. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// * `NotSupported`, if the vGPU doesn't support this feature or accounting mode is disabled + /// * `DriverNotLoaded`, driver is not running on the vGPU instance + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetAccountingMode")] pub fn get_accounting_mode(&self) -> Result { let sym = nvml_sym( @@ -550,6 +585,13 @@ impl<'dev> VgpuInstance<'dev> { Ok(mode == nvmlEnableState_enum_NVML_FEATURE_ENABLED) } + /// Retrieve the current ECC mode of vGPU instance. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// * `NotSupported`, if the vGPU doesn't support this feature or accounting mode is disabled #[doc(alias = "nvmlVgpuInstanceGetEccMode")] pub fn get_ecc_mode(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetEccMode.as_ref())?; @@ -560,6 +602,17 @@ impl<'dev> VgpuInstance<'dev> { Ok(mode == nvmlEnableState_enum_NVML_FEATURE_ENABLED) } + /// Retrieve the encoder capacity of a vGPU instance, as a percentage of maximum encoder + /// capacity with valid values in the range 0-100. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetEncoderCapacity")] pub fn get_encoder_capacity(&self) -> Result { let sym = nvml_sym( @@ -576,6 +629,16 @@ impl<'dev> VgpuInstance<'dev> { Ok(cap) } + /// Retrieves information about all active encoder sessions on a vGPU Instance. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetEncoderSessions")] pub fn get_encoder_sessions(&self) -> Result, NvmlError> { let sym = nvml_sym( @@ -597,6 +660,16 @@ impl<'dev> VgpuInstance<'dev> { .collect() } + /// Retrieves the current encoder statistics of a vGPU Instance + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetEncoderStats")] pub fn get_encoder_stats(&self) -> Result { let sym = nvml_sym( @@ -624,6 +697,20 @@ impl<'dev> VgpuInstance<'dev> { }) } + /// Retrieves information about active frame buffer capture sessions on a vGPU Instance. + /// + /// > hResolution, vResolution, averageFPS and averageLatency data for a FBC session + /// > returned in sessionInfo may be zero if there are no new frames captured since the + /// > session started. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetFBCSessions")] pub fn get_fbc_sessions(&self) -> Result, NvmlError> { let sym = nvml_sym( @@ -646,6 +733,16 @@ impl<'dev> VgpuInstance<'dev> { info.into_iter().map(FbcSessionInfo::try_from).collect() } + /// Retrieves the active frame buffer capture sessions statistics of a vGPU Instance + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetFBCStats")] pub fn get_fbc_stats(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetFBCStats.as_ref())?; @@ -656,6 +753,19 @@ impl<'dev> VgpuInstance<'dev> { } } + /// Retrieve the frame rate limit set for the vGPU instance. + /// + /// Returns the value of the frame rate limit set for the vGPU instance + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotSupported`, if frame rate limiter is turned off for the vGPU type + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Kepler or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetFrameRateLimit")] pub fn get_frame_rate_limit(&self) -> Result { let sym = nvml_sym( @@ -672,6 +782,17 @@ impl<'dev> VgpuInstance<'dev> { Ok(limit) } + /// Retrieve the GPU Instance ID for the given vGPU Instance. The API will return a valid GPU + /// Instance ID for MIG backed vGPU Instance, else INVALID_GPU_INSTANCE_ID is returned. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Kepler or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetGpuInstanceId")] pub fn get_gpu_instance_id(&self) -> Result { let sym = nvml_sym( @@ -688,6 +809,17 @@ impl<'dev> VgpuInstance<'dev> { Ok(id) } + /// Retrieves the PCI Id of the given vGPU Instance i.e. the PCI Id of the GPU as seen inside + /// the VM. + /// + /// The vGPU PCI id is returned as "00000000:00:00.0" if NVIDIA driver is not installed on the + /// vGPU instance. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// * `DriverNotLoaded`, driver is not running on the vGPU instance #[doc(alias = "nvmlVgpuInstanceGetGpuPciId")] pub fn get_gpu_pci_id(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetGpuPciId.as_ref())?; @@ -705,6 +837,13 @@ impl<'dev> VgpuInstance<'dev> { Ok(raw_id.to_str()?.to_string()) } + /// Query the license information of the vGPU instance. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// * `DriverNotLoaded`, driver is not running on the vGPU instance #[cfg(feature = "legacy-functions")] #[doc(alias = "nvmlVgpuInstanceGetLicenseInfo")] pub fn get_license_info(&self) -> Result { @@ -724,6 +863,17 @@ impl<'dev> VgpuInstance<'dev> { Ok(VgpuLicenseInfo::from(info)) } + /// Query the license information of the vGPU instance. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// * `DriverNotLoaded`, driver is not running on the vGPU instance + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetLicenseInfo_v2")] pub fn get_license_info_v2(&self) -> Result { let sym = nvml_sym( @@ -742,6 +892,22 @@ impl<'dev> VgpuInstance<'dev> { Ok(VgpuLicenseInfo::from(info)) } + /// Retrieve the MDEV UUID of a vGPU instance. + /// + /// The MDEV UUID is a globally unique identifier of the mdev device assigned to the VM, and is + /// returned as a 5-part hexadecimal string, not exceeding 80 characters in length (including + /// the NULL terminator). MDEV UUID is displayed only on KVM platform. + /// See nvmlConstants::NVML_DEVICE_UUID_BUFFER_SIZE. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// * `NotSupported`, on any hypervisor other than KVM + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetMdevUUID")] pub fn get_mdev_uuid(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetMdevUUID.as_ref())?; @@ -759,6 +925,23 @@ impl<'dev> VgpuInstance<'dev> { } } + /// Returns vGPU metadata structure for a running vGPU. The structure contains information + /// about the vGPU and its associated VM such as the currently installed NVIDIA guest driver + /// version, together with host driver version and an opaque data section containing internal + /// state. + /// + /// May be called at any time for a vGPU instance. Some fields in the returned structure are + /// dependent on information obtained from the guest VM, which may not yet have reached a state + /// where that information is available. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// #[doc(alias = "nvmlVgpuInstanceGetMetadata")] pub fn get_metadata(&self) -> Result, NvmlError> { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetMetadata.as_ref())?; @@ -772,6 +955,14 @@ impl<'dev> VgpuInstance<'dev> { metadata.into_iter().map(VgpuMetadata::try_from).collect() } + /// Query the placement ID of active vGPU instance. + /// + /// When in vGPU heterogeneous mode, this function returns a valid placement ID as + /// [`VgpuPlacementId::id`], [`VgpuPlacementId::version`] is the version number + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system #[doc(alias = "nvmlVgpuInstanceGetPlacementId")] pub fn get_get_placement_id(&self) -> Result { let sym = nvml_sym( @@ -789,6 +980,20 @@ impl<'dev> VgpuInstance<'dev> { Ok(raw_placement_id.into()) } + /// Retrieve the currently used runtime state size of the vGPU instance + /// + /// This size represents the maximum in-memory data size utilized by a vGPU instance during + /// standard operation. This measurement is exclusive of frame buffer (FB) data size assigned + /// to the vGPU instance. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetRuntimeStateSize")] pub fn get_get_runtime_state_size(&self) -> Result { let sym = nvml_sym( @@ -806,6 +1011,18 @@ impl<'dev> VgpuInstance<'dev> { Ok(raw_state.into()) } + /// Retrieve the UUID of a vGPU instance. + /// + /// The UUID is a globally unique identifier associated with the vGPU, and is returned as a 5-part hexadecimal string + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Kepler or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetUUID")] pub fn get_uuid(&self) -> Result { let sym = nvml_sym(self.device.nvml().lib.nvmlVgpuInstanceGetUUID.as_ref())?; @@ -823,6 +1040,23 @@ impl<'dev> VgpuInstance<'dev> { } } + /// Retrieve the NVIDIA driver version installed in the VM associated with a vGPU. + /// + /// The version is returned as an alphanumeric string in the caller-supplied buffer version. + /// This may be called at any time for a vGPU instance. + /// + /// The guest VM driver version is returned as "Not Available" if no NVIDIA driver is installed + /// in the VM, or the VM has not yet booted to the point where the NVIDIA driver is loaded and + /// initialized. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Kepler or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceGetVmDriverVersion")] pub fn get_driver_version(&self) -> Result { let sym = nvml_sym( @@ -845,7 +1079,16 @@ impl<'dev> VgpuInstance<'dev> { Ok(raw_id.to_str()?.to_string()) } } - + /// Set the encoder capacity of a vGPU instance, as a percentage of maximum encoder capacity with valid values in the range 0-100. + /// + /// # Errors + /// + /// * `Uninitialized`, if the library has not been successfully initialized + /// * `NotFound`, if the vGPU does not match a valid active vGPU instance on the system + /// + /// # Platform Support + /// + /// For Maxwell or newer fully supported devices. #[doc(alias = "nvmlVgpuInstanceSetEncoderCapacity")] pub fn set_encoder_capacity(&self, capacity: u32) -> Result<(), NvmlError> { let sym = nvml_sym( From c6b1acb776eab55a46dca923e2091a7c250a2d37 Mon Sep 17 00:00:00 2001 From: Robert Masen Date: Fri, 6 Mar 2026 18:44:45 +0000 Subject: [PATCH 4/4] add vGpu Tests --- nvml-wrapper/src/struct_wrappers/vgpu.rs | 4 + nvml-wrapper/src/test_utils.rs | 17 ++ nvml-wrapper/src/vgpu.rs | 263 +++++++++++++++++++++++ 3 files changed, 284 insertions(+) diff --git a/nvml-wrapper/src/struct_wrappers/vgpu.rs b/nvml-wrapper/src/struct_wrappers/vgpu.rs index d820569..57e7078 100644 --- a/nvml-wrapper/src/struct_wrappers/vgpu.rs +++ b/nvml-wrapper/src/struct_wrappers/vgpu.rs @@ -7,6 +7,7 @@ use crate::{ error::NvmlError, }; +#[derive(Debug, Copy, Clone)] pub struct VgpuLicenseInfo { pub is_licensed: bool, pub expiry: VgpuLicenseExpiry, @@ -23,6 +24,7 @@ impl From for VgpuLicenseInfo { } } +#[derive(Debug, Copy, Clone)] pub struct VgpuLicenseExpiry { pub year: u16, pub month: u8, @@ -47,6 +49,7 @@ impl From for VgpuLicenseExpiry { } } +#[derive(Debug)] pub struct VgpuMetadata { pub version: u32, pub revision: u32, @@ -115,6 +118,7 @@ impl From for VgpuRuntimeState { } } +#[derive(Debug, Clone, Copy)] pub struct Bar1Info { pub version: u32, pub size: u64, diff --git a/nvml-wrapper/src/test_utils.rs b/nvml-wrapper/src/test_utils.rs index a151016..173ef89 100644 --- a/nvml-wrapper/src/test_utils.rs +++ b/nvml-wrapper/src/test_utils.rs @@ -7,6 +7,7 @@ use crate::Unit; use crate::bitmasks::{device::*, event::*}; use crate::enum_wrappers::device::*; +use crate::enum_wrappers::vgpu::VmId; use crate::enums::device::BusType; use crate::enums::device::DeviceArchitecture; use crate::enums::device::PcieLinkMaxSpeed; @@ -15,7 +16,13 @@ use crate::enums::unit::*; use crate::error::NvmlError; use crate::event::EventSet; use crate::struct_wrappers::gpm::GpmMetricResult; +use crate::struct_wrappers::vgpu::Bar1Info; +use crate::struct_wrappers::vgpu::VgpuLicenseInfo; +use crate::struct_wrappers::vgpu::VgpuMetadata; +use crate::struct_wrappers::vgpu::VgpuPlacementId; +use crate::struct_wrappers::vgpu::VgpuRuntimeState; use crate::vgpu::VgpuInstance; +use crate::vgpu::VgpuType; use std::fmt::Debug; use crate::struct_wrappers::nv_link::*; @@ -69,6 +76,7 @@ impl ShouldPrint for bool {} impl ShouldPrint for u32 {} impl ShouldPrint for i32 {} impl ShouldPrint for (u32, u32) {} +impl ShouldPrint for (u64, u64) {} impl ShouldPrint for u64 {} impl ShouldPrint for String {} impl ShouldPrint for Brand {} @@ -127,6 +135,15 @@ impl ShouldPrint for ProfileInfo {} impl ShouldPrint for GspFirmwareMode {} impl<'dev> ShouldPrint for VgpuInstance<'dev> {} impl<'dev> ShouldPrint for Vec> {} +impl ShouldPrint for VmId {} +impl ShouldPrint for VgpuLicenseInfo {} +impl ShouldPrint for VgpuMetadata {} +impl ShouldPrint for Vec {} +impl<'dev> ShouldPrint for VgpuType<'dev> {} +impl<'dev> ShouldPrint for Vec> {} +impl ShouldPrint for Bar1Info {} +impl ShouldPrint for VgpuPlacementId {} +impl ShouldPrint for VgpuRuntimeState {} #[cfg(target_os = "windows")] impl ShouldPrint for DriverModelState {} diff --git a/nvml-wrapper/src/vgpu.rs b/nvml-wrapper/src/vgpu.rs index f95117f..c12c0cb 100644 --- a/nvml-wrapper/src/vgpu.rs +++ b/nvml-wrapper/src/vgpu.rs @@ -25,6 +25,7 @@ use crate::{ Device, }; +#[derive(Debug)] pub struct VgpuType<'dev> { id: nvmlVgpuTypeId_t, device: &'dev Device<'dev>, @@ -1128,3 +1129,265 @@ impl<'dev> std::fmt::Debug for VgpuInstance<'dev> { .finish_non_exhaustive() } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::test_utils::*; + + #[test] + fn vgpu_type_class_name() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).class_name()); + } + + #[test] + fn vgpu_type_license() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).license()); + } + + #[test] + fn vgpu_type_name() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).name()); + } + + #[test] + fn vgpu_type_device_id() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).device_id()); + } + + #[test] + fn vgpu_type_frame_rate_limit() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).frame_rate_limit()); + } + + #[test] + fn vgpu_type_framebuffer_size() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).framebuffer_size()); + } + + #[test] + fn vgpu_type_instance_profile_id() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).instance_profile_id()); + } + + #[test] + fn vgpu_type_max_instances() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).max_instances()); + } + + #[test] + fn vgpu_type_max_instances_per_vm() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).max_instances_per_vm()); + } + + #[test] + fn vgpu_type_num_display_heads() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).num_display_heads()); + } + + #[test] + fn vgpu_type_resolution() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).resolution(1)); + } + + #[test] + fn vgpu_type_get_bar1_info() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).get_bar1_info()); + } + + #[test] + fn vgpu_type_get_fb_reservation() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).get_fb_reservation()); + } + + #[test] + fn vgpu_type_get_gsp_heap_size() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuType::new(dev, 1).get_gsp_heap_size()); + } + + #[test] + fn vgpu_instance_get_vm_id() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_vm_id()); + } + + #[test] + fn vgpu_instance_get_fb_usage() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_fb_usage()); + } + + #[test] + fn vgpu_instance_get_instance_type() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + Ok(VgpuInstance::new(1, dev).get_instance_type()?.id) + }); + } + + #[test] + fn vgpu_instance_accounting_pids() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).accounting_pids()); + } + + #[test] + fn vgpu_instance_clear_accounting_pids() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).clear_accounting_pids() + }); + } + + #[test] + fn vgpu_instance_get_accounting_mode() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_accounting_mode() + }); + } + + #[test] + fn vgpu_instance_get_ecc_mode() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_ecc_mode()); + } + + #[test] + fn vgpu_instance_get_encoder_capacity() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_encoder_capacity() + }); + } + + #[test] + fn vgpu_instance_get_encoder_sessions() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_encoder_sessions() + }); + } + + #[test] + fn vgpu_instance_get_encoder_stats() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_encoder_stats() + }); + } + + #[test] + fn vgpu_instance_get_fbc_sessions() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_fbc_sessions()); + } + + #[test] + fn vgpu_instance_get_fbc_stats() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_fbc_stats()); + } + + #[test] + fn vgpu_instance_get_frame_rate_limit() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_frame_rate_limit() + }); + } + + #[test] + fn vgpu_instance_get_gpu_instance_id() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_gpu_instance_id() + }); + } + + #[test] + fn vgpu_instance_get_gpu_pci_id() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_gpu_pci_id()); + } + + #[test] + #[cfg(feature = "legacy-functions")] + fn vgpu_instance_get_license_info() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_license_info()); + } + + #[test] + fn vgpu_instance_get_license_info_v2() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_license_info_v2() + }); + } + + #[test] + fn vgpu_instance_get_mdev_uuid() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_mdev_uuid()); + } + + #[test] + fn vgpu_instance_get_metadata() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_metadata()); + } + + #[test] + fn vgpu_instance_get_get_placement_id() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_get_placement_id() + }); + } + + #[test] + fn vgpu_instance_get_get_runtime_state_size() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_get_runtime_state_size() + }); + } + + #[test] + fn vgpu_instance_get_uuid() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| VgpuInstance::new(1, dev).get_uuid()); + } + + #[test] + fn vgpu_instance_get_driver_version() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).get_driver_version() + }); + } + + #[test] + fn vgpu_instance_set_encoder_capacity() { + let nvml = nvml(); + test_with_device(1, &nvml, |dev| { + VgpuInstance::new(1, dev).set_encoder_capacity(50) + }); + } +}