use std::os::raw::c_uint;
use nvml_wrapper::Nvml;
use nvml_wrapper::error::nvml_try;
use crate::device::types::{MigGpuInfo, MigInstanceInfo};
use crate::utils::get_hostname;
const MAX_MIG_INSTANCES_PER_DEVICE: u32 = 64;
const MIG_NAME_BUFFER: usize = 64;
pub fn collect_mig_info(nvml: &Nvml) -> Vec<MigGpuInfo> {
let mut out = Vec::new();
let device_count = match nvml.device_count() {
Ok(n) => n,
Err(_) => return out,
};
let hostname = get_hostname();
for index in 0..device_count {
let device = match nvml.device_by_index(index) {
Ok(d) => d,
Err(_) => continue,
};
let mode = match device.mig_mode() {
Ok(m) => m,
Err(_) => continue,
};
let mig_enabled = mode.current == 1;
let gpu_uuid = device.uuid().unwrap_or_else(|_| format!("GPU-{index}"));
let gpu_name = device.name().unwrap_or_else(|_| "Unknown GPU".to_string());
let instances = if mig_enabled {
enumerate_mig_instances(nvml, &device)
} else {
Vec::new()
};
out.push(MigGpuInfo {
host_id: hostname.clone(),
hostname: hostname.clone(),
instance: hostname.clone(),
gpu_index: index,
gpu_uuid,
gpu_name,
mig_mode: mig_enabled,
instances,
});
}
out
}
fn enumerate_mig_instances(nvml: &Nvml, parent: &nvml_wrapper::Device) -> Vec<MigInstanceInfo> {
let max_count = match parent.mig_device_count() {
Ok(c) => c.min(MAX_MIG_INSTANCES_PER_DEVICE),
Err(_) => return Vec::new(),
};
let mut out = Vec::with_capacity(max_count as usize);
for slot in 0..max_count {
let mig_device = match parent.mig_device_by_index(slot) {
Ok(d) => d,
Err(_) => continue,
};
out.push(collect_single_mig_instance(nvml, &mig_device, slot));
}
out
}
fn collect_single_mig_instance(
nvml: &Nvml,
device: &nvml_wrapper::Device,
instance_id: u32,
) -> MigInstanceInfo {
let uuid = device.uuid().unwrap_or_default();
let mem = device.memory_info().ok();
let memory_used_bytes = mem.as_ref().map(|m| m.used).unwrap_or(0);
let memory_total_bytes = mem.as_ref().map(|m| m.total).unwrap_or(0);
let util = device.utilization_rates().ok();
let utilization_gpu = util.as_ref().map(|u| u.gpu);
let utilization_memory = util.as_ref().map(|u| u.memory);
let gpu_instance_id = mig_gpu_instance_id(nvml, device);
let compute_instance_id = mig_compute_instance_id(nvml, device);
let profile_name = device
.name()
.ok()
.map(|s| extract_profile_suffix(&s))
.unwrap_or_default();
MigInstanceInfo {
instance_id,
gpu_instance_id,
compute_instance_id,
uuid,
profile_name,
utilization_gpu,
utilization_memory,
memory_used_bytes,
memory_total_bytes,
}
}
fn mig_gpu_instance_id(nvml: &Nvml, device: &nvml_wrapper::Device) -> Option<u32> {
let sym = nvml.lib().nvmlDeviceGetGpuInstanceId.as_ref().ok()?;
let mut id: c_uint = 0;
let result = unsafe { sym(device.handle(), &mut id) };
nvml_try(result).ok()?;
Some(id)
}
fn mig_compute_instance_id(nvml: &Nvml, device: &nvml_wrapper::Device) -> Option<u32> {
let sym = nvml.lib().nvmlDeviceGetComputeInstanceId.as_ref().ok()?;
let mut id: c_uint = 0;
let result = unsafe { sym(device.handle(), &mut id) };
nvml_try(result).ok()?;
Some(id)
}
fn extract_profile_suffix(raw: &str) -> String {
for token in raw.split_whitespace() {
if is_mig_profile_token(token) {
return token.to_string();
}
}
raw.to_string()
}
fn is_mig_profile_token(token: &str) -> bool {
let token = token.trim();
if token.is_empty() || token.len() > MIG_NAME_BUFFER {
return false;
}
if !token.contains("g.") || !token.ends_with("gb") {
return false;
}
token.chars().next().is_some_and(|c| c.is_ascii_digit())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extract_profile_suffix_returns_canonical_token() {
assert_eq!(extract_profile_suffix("MIG 1g.5gb Device"), "1g.5gb");
assert_eq!(extract_profile_suffix("NVIDIA A100 MIG 7g.40gb"), "7g.40gb");
assert_eq!(extract_profile_suffix("3g.20gb"), "3g.20gb");
}
#[test]
fn extract_profile_suffix_returns_input_when_no_token_present() {
assert_eq!(
extract_profile_suffix("Some Random Name"),
"Some Random Name"
);
assert_eq!(extract_profile_suffix(""), "");
}
#[test]
fn is_mig_profile_token_accepts_valid_slices() {
assert!(is_mig_profile_token("1g.5gb"));
assert!(is_mig_profile_token("2g.10gb"));
assert!(is_mig_profile_token("3g.20gb"));
assert!(is_mig_profile_token("7g.40gb"));
assert!(is_mig_profile_token("7g.80gb"));
}
#[test]
fn is_mig_profile_token_rejects_non_profiles() {
assert!(!is_mig_profile_token(""));
assert!(!is_mig_profile_token("MIG"));
assert!(!is_mig_profile_token("1g.5g"));
assert!(!is_mig_profile_token("g.5gb"));
assert!(!is_mig_profile_token("xg.ygb"));
}
}