Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions nvml-wrapper/src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -6174,18 +6176,25 @@ impl<'nvml> Device<'nvml> {
// Tested
#[cfg(target_os = "linux")]
#[doc(alias = "nvmlDeviceGetActiveVgpus")]
pub fn active_vgpus(&self) -> Result<Vec<nvmlVgpuInstance_t>, NvmlError> {
pub fn active_vgpus<'a>(&'a self) -> Result<Vec<VgpuInstance<'nvml>>, 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<nvmlVgpuInstance_t> = 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())
}

/**
Expand Down Expand Up @@ -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::<Vec<_>>())
})
Comment on lines +7963 to +7968
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a lifetime generic argument in the return value here made these test helpers fail due to the lifetime requirements, if there is a better way to handle this please let me know

}

#[test]
Expand Down
1 change: 1 addition & 0 deletions nvml-wrapper/src/enum_wrappers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool, NvmlError> {
match state {
Expand Down
47 changes: 47 additions & 0 deletions nvml-wrapper/src/enum_wrappers/vgpu.rs
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a misinterpretation of the *_wrappers modules, let me know if you'd prefer this is moved to a different location

Domain(String),
Uuid(String),
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u32)]
pub enum VgpuLicenseState {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit of an odd scenario since it is effectively a C enum in NVML but no c enum is actually defined for it. Let me know if you'd prefer this live in src/vgpu.rs instead of here

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<u32> for VgpuLicenseState {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since one of the cases was NVML_GRID_LICENSE_STATE_UNKNOWN, it seemed like we wouldn't need TryFrom here but let me know if you'd prefer to use the UnexpectedVariant error instead

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,
}
1 change: 1 addition & 0 deletions nvml-wrapper/src/struct_wrappers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
134 changes: 134 additions & 0 deletions nvml-wrapper/src/struct_wrappers/vgpu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use std::os::raw::c_char;

use ffi::bindings::*;

use crate::{
enum_wrappers::vgpu::{VgpuGuestInfoState, VgpuLicenseState},
error::NvmlError,
};

#[derive(Debug, Copy, Clone)]
pub struct VgpuLicenseInfo {
pub is_licensed: bool,
pub expiry: VgpuLicenseExpiry,
pub state: VgpuLicenseState,
}

impl From<nvmlVgpuLicenseInfo_t> for VgpuLicenseInfo {
fn from(value: nvmlVgpuLicenseInfo_t) -> Self {
Self {
is_licensed: value.isLicensed != 0,
expiry: VgpuLicenseExpiry::from(value.licenseExpiry),
state: VgpuLicenseState::from(value.currentState),
}
}
}

#[derive(Debug, Copy, Clone)]
pub struct VgpuLicenseExpiry {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub min: u8,
pub sec: u8,
pub status: u8,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find any documentation about why are expected values here and left the type effectively unchanged. Let me know if there is something I missed and I can add another enum to capture the status.

}

impl From<nvmlVgpuLicenseExpiry_t> 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),
Comment on lines +41 to +46
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These all seem like there would be little risk to fail and the TryFromIntError these return wasn't already included in the error variants, let me know if you'd prefer another variant is added there instead of using these defaults

status: value.status,
}
}
}

#[derive(Debug)]
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<nvmlVgpuMetadata_t> for VgpuMetadata {
type Error = NvmlError;
fn try_from(value: nvmlVgpuMetadata_t) -> Result<Self, Self::Error> {
let convert_c_str = |c_str: &[c_char]| {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is a bit odd, I wanted to avoid using any unsafe and so this will manually copy the string byte by byte which may not be the most efficient. Let me know if you'd prefer a different method for this or even if lossy conversion is acceptable here

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::<String, NvmlError>(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<nvmlVgpuPlacementId_t> 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<nvmlVgpuRuntimeState_t> for VgpuRuntimeState {
fn from(value: nvmlVgpuRuntimeState_t) -> Self {
Self {
version: value.version,
size: value.size,
}
}
}

#[derive(Debug, Clone, Copy)]
pub struct Bar1Info {
pub version: u32,
pub size: u64,
}

impl From<nvmlVgpuTypeBar1Info_v1_t> for Bar1Info {
fn from(value: nvmlVgpuTypeBar1Info_v1_t) -> Self {
Self {
version: value.version,
size: value.bar1Size,
}
}
}
24 changes: 22 additions & 2 deletions nvml-wrapper/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -15,6 +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::*;
Expand Down Expand Up @@ -68,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 {}
Expand Down Expand Up @@ -124,6 +133,17 @@ impl ShouldPrint for Vec<GpuInstancePlacement> {}
impl ShouldPrint for (VgpuVersion, VgpuVersion) {}
impl ShouldPrint for ProfileInfo {}
impl ShouldPrint for GspFirmwareMode {}
impl<'dev> ShouldPrint for VgpuInstance<'dev> {}
impl<'dev> ShouldPrint for Vec<VgpuInstance<'dev>> {}
impl ShouldPrint for VmId {}
impl ShouldPrint for VgpuLicenseInfo {}
impl ShouldPrint for VgpuMetadata {}
impl ShouldPrint for Vec<VgpuMetadata> {}
impl<'dev> ShouldPrint for VgpuType<'dev> {}
impl<'dev> ShouldPrint for Vec<VgpuType<'dev>> {}
impl ShouldPrint for Bar1Info {}
impl ShouldPrint for VgpuPlacementId {}
impl ShouldPrint for VgpuRuntimeState {}

#[cfg(target_os = "windows")]
impl ShouldPrint for DriverModelState {}
Expand Down Expand Up @@ -151,9 +171,9 @@ where
multi(reps, test);
}

pub fn test_with_device<T, R>(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<R, NvmlError>,
T: Fn(&Device<'a>) -> Result<R, NvmlError>,
R: ShouldPrint,
{
let device = device(nvml);
Expand Down
Loading
Loading