#![allow(dead_code)] #![allow(non_camel_case_types)]
mod api;
mod apply;
pub(crate) mod ffi;
mod loader;
mod point;
mod refresh;
#[cfg(test)]
mod tests;
pub use apply::{ApplyPlatform, apply_to_gpu_info};
#[allow(unused_imports)]
pub use loader::normalise_pci_bdf;
pub use loader::prepare_sysman_env_for_legacy_runtime;
pub(crate) use loader::with_runtime;
pub use loader::{LevelZeroInit, LevelZeroProbe, SysmanRoute, probe};
pub(crate) use point::{
FanSample, FrequencySample, MemorySample, TemperatureSample, populate_point_samples,
refresh_fan, refresh_frequency, refresh_memory, refresh_temperature,
};
pub(crate) use refresh::{
EngineSample, PowerSample, populate_engine_samples, populate_power_samples, refresh_engines,
refresh_power,
};
#[derive(Debug, Default)]
pub struct LevelZeroState {
pub(crate) bind_attempted: bool,
pub(crate) device: Option<loader::zes_device_handle_t_send>,
pub(crate) engine_samples: Vec<EngineSample>,
pub(crate) power_samples: Vec<PowerSample>,
pub(crate) temperature_samples: Vec<TemperatureSample>,
pub(crate) memory_samples: Vec<MemorySample>,
pub(crate) frequency_samples: Vec<FrequencySample>,
pub(crate) fan_samples: Vec<FanSample>,
}
impl LevelZeroState {
pub fn empty() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum MetricStatus {
#[default]
Unsupported,
Unavailable,
Seeded,
Fresh,
}
#[derive(Debug, Clone, Default)]
pub struct LevelZeroDiagnostics {
pub engine: MetricStatus,
pub power: MetricStatus,
pub temperature: MetricStatus,
pub memory: MetricStatus,
pub frequency: MetricStatus,
pub fan: MetricStatus,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FreshValue<T> {
pub value: T,
pub source: &'static str,
}
impl<T> FreshValue<T> {
pub(crate) fn level_zero(value: T) -> Self {
Self {
value,
source: "Level Zero Sysman",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LevelZeroMemoryKind {
DedicatedLocal,
SharedSystem,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LevelZeroMemoryReadout {
pub used_bytes: u64,
pub total_bytes: u64,
pub kind: LevelZeroMemoryKind,
pub source: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LevelZeroFanReadout {
pub rpm: Option<u32>,
pub percent: Option<u32>,
pub source: &'static str,
}
#[derive(Debug, Clone, Default)]
pub struct LevelZeroReadout {
pub engines: Vec<(&'static str, f64)>,
pub primary_engine_utilization: Option<FreshValue<f64>>,
pub power_watts: Option<FreshValue<f64>>,
pub temperature_celsius: Option<FreshValue<u32>>,
pub memory: Option<LevelZeroMemoryReadout>,
pub frequency_mhz: Option<FreshValue<u32>>,
pub frequency_domains: Vec<(&'static str, u32)>,
pub fan: Option<LevelZeroFanReadout>,
pub diagnostics: LevelZeroDiagnostics,
}
impl LevelZeroReadout {
pub fn has_fresh_data(&self) -> bool {
!self.engines.is_empty()
|| self.primary_engine_utilization.is_some()
|| self.power_watts.is_some()
|| self.temperature_celsius.is_some()
|| self.memory.is_some()
|| self.frequency_mhz.is_some()
|| self.fan.is_some()
}
}
pub fn refresh(state: &mut LevelZeroState, pci_bdf: &str) -> Option<LevelZeroReadout> {
with_runtime(|runtime| {
if !state.bind_attempted {
state.bind_attempted = true;
state.device = runtime.devices_by_pci.get(pci_bdf).copied();
if state.device.is_some() {
populate_engine_samples(&runtime.api, state);
populate_power_samples(&runtime.api, state);
populate_point_samples(&runtime.api, state);
}
}
state.device?;
let mut out = LevelZeroReadout::default();
let engines = refresh_engines(&runtime.api, state);
if let Some(primary) = engines.primary {
out.primary_engine_utilization = Some(FreshValue::level_zero(primary));
out.diagnostics.engine = MetricStatus::Fresh;
} else if engines.seeded {
out.diagnostics.engine = MetricStatus::Seeded;
} else if state.engine_samples.is_empty() {
out.diagnostics.engine = MetricStatus::Unsupported;
} else {
out.diagnostics.engine = MetricStatus::Unavailable;
}
if !engines.entries.is_empty() {
out.engines = engines.entries;
}
if let Some(watts) = refresh_power(&runtime.api, state) {
out.power_watts = Some(FreshValue::level_zero(watts));
out.diagnostics.power = MetricStatus::Fresh;
} else if !state.power_samples.is_empty() {
out.diagnostics.power = MetricStatus::Seeded;
}
if let Some(temp) = refresh_temperature(&runtime.api, state) {
out.temperature_celsius = Some(temp);
out.diagnostics.temperature = MetricStatus::Fresh;
} else if !state.temperature_samples.is_empty() {
out.diagnostics.temperature = MetricStatus::Unavailable;
}
if let Some(memory) = refresh_memory(&runtime.api, state) {
out.memory = Some(memory);
out.diagnostics.memory = match memory.kind {
LevelZeroMemoryKind::DedicatedLocal => MetricStatus::Fresh,
LevelZeroMemoryKind::SharedSystem => MetricStatus::Unavailable,
};
} else if !state.memory_samples.is_empty() {
out.diagnostics.memory = MetricStatus::Unavailable;
}
let (frequency, domains) = refresh_frequency(&runtime.api, state);
out.frequency_domains = domains;
if let Some(freq) = frequency {
out.frequency_mhz = Some(freq);
out.diagnostics.frequency = MetricStatus::Fresh;
} else if !state.frequency_samples.is_empty() {
out.diagnostics.frequency = MetricStatus::Unavailable;
}
if let Some(fan) = refresh_fan(&runtime.api, state) {
out.fan = Some(fan);
out.diagnostics.fan = MetricStatus::Fresh;
} else if !state.fan_samples.is_empty() {
out.diagnostics.fan = MetricStatus::Unavailable;
}
Some(out)
})
.flatten()
}
pub(crate) fn engine_label(group: i32) -> &'static str {
match group {
ffi::ZES_ENGINE_GROUP_COMPUTE_SINGLE => "compute (XMX)",
ffi::ZES_ENGINE_GROUP_RENDER_SINGLE => "render",
ffi::ZES_ENGINE_GROUP_COPY_SINGLE => "copy",
ffi::ZES_ENGINE_GROUP_MEDIA_DECODE_SINGLE => "media-decode",
ffi::ZES_ENGINE_GROUP_MEDIA_ENCODE_SINGLE => "media-encode",
_ => "other",
}
}
pub(crate) fn is_tracked_engine(group: i32) -> bool {
matches!(
group,
ffi::ZES_ENGINE_GROUP_COMPUTE_SINGLE
| ffi::ZES_ENGINE_GROUP_RENDER_SINGLE
| ffi::ZES_ENGINE_GROUP_COPY_SINGLE
| ffi::ZES_ENGINE_GROUP_MEDIA_DECODE_SINGLE
| ffi::ZES_ENGINE_GROUP_MEDIA_ENCODE_SINGLE
)
}
pub(crate) fn label_order(class: &str) -> u8 {
match class {
"render" => 0,
"compute (XMX)" => 1,
"copy" => 2,
"media-decode" => 3,
"media-encode" => 4,
_ => 5,
}
}
pub(crate) fn primary_utilization(engines: &[(&'static str, f64)]) -> Option<f64> {
if engines.is_empty() {
return None;
}
let busy_compute = engines
.iter()
.filter(|(l, _)| *l == "render" || *l == "compute (XMX)")
.map(|(_, p)| *p)
.fold(f64::NEG_INFINITY, f64::max);
if busy_compute.is_finite() {
return Some(busy_compute);
}
Some(engines.iter().map(|(_, p)| *p).fold(0.0_f64, f64::max))
}
pub fn engine_count(state: &LevelZeroState) -> usize {
state.engine_samples.len()
}
pub fn enumerated_pci_bdfs() -> Vec<String> {
with_runtime(|runtime| {
let mut keys: Vec<String> = runtime.devices_by_pci.keys().cloned().collect();
keys.sort();
keys
})
.unwrap_or_default()
}
pub fn power_domain_count(state: &LevelZeroState) -> usize {
state.power_samples.len()
}
pub fn is_bound(state: &LevelZeroState) -> bool {
state.device.is_some()
}
pub(crate) fn sort_engine_entries(engines: &mut [(&'static str, f64)]) {
engines.sort_by(|a, b| label_order(a.0).cmp(&label_order(b.0)).then(a.0.cmp(b.0)));
}