use super::api::{LoadedLibrary, LzApi};
use super::ffi;
use libloading::Library;
use once_cell::sync::OnceCell;
use std::collections::HashMap;
use std::ffi::c_void;
use std::sync::{Mutex, Once};
use tracing::{debug, warn};
pub use super::api::try_load_library;
pub(crate) const MAX_L0_HANDLES: usize = 256;
pub(crate) fn cap_handle_count(reported: u32, what: &'static str) -> (usize, u32) {
let safe = (reported as usize).min(MAX_L0_HANDLES);
if (reported as usize) > MAX_L0_HANDLES {
L0_CAP_WARN.call_once(|| {
warn!(
"Level Zero: driver reported {reported} {what}, capping at {MAX_L0_HANDLES}; \
further over-cap counts will be silently truncated"
);
});
}
(safe, safe as u32)
}
static L0_CAP_WARN: Once = Once::new();
#[cfg(target_os = "linux")]
pub(crate) const LIBZE_PATHS: &[&str] = &[
"libze_loader.so.1",
"libze_loader.so",
"/usr/lib/x86_64-linux-gnu/libze_loader.so.1",
"/usr/lib/x86_64-linux-gnu/libze_loader.so",
"/usr/lib64/libze_loader.so.1",
"/usr/lib64/libze_loader.so",
"/usr/local/lib/libze_loader.so.1",
];
#[cfg(target_os = "windows")]
pub(crate) const LIBZE_PATHS: &[&str] = &[
"ze_loader.dll",
"C:\\Windows\\System32\\ze_loader.dll",
];
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
pub(crate) const LIBZE_PATHS: &[&str] = &[];
pub(crate) const SYSMAN_ENV_KEY: &str = "ZES_ENABLE_SYSMAN";
static SYSMAN_ENV_INIT: Once = Once::new();
pub unsafe fn prepare_sysman_env_for_legacy_runtime() {
SYSMAN_ENV_INIT.call_once(|| {
unsafe {
if std::env::var_os(SYSMAN_ENV_KEY).is_none() {
std::env::set_var(SYSMAN_ENV_KEY, "1");
}
}
});
}
static LZ_RUNTIME: OnceCell<Mutex<Option<LzRuntime>>> = OnceCell::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LevelZeroInit {
LoaderMissing,
SysmanUnavailable,
ZeInitFailed(i32),
ZesInitFailed(i32),
Ok,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SysmanRoute {
ZesInit,
LegacyEnvVar,
}
#[derive(Debug, Clone)]
pub struct LevelZeroProbe {
pub compiled_in: bool,
pub searched_paths: &'static [&'static str],
pub loaded_path: Option<&'static str>,
pub init: LevelZeroInit,
pub sysman_route: Option<SysmanRoute>,
pub device_count: usize,
pub device_bdfs: Vec<String>,
}
static LZ_INIT_RECORD: OnceCell<(Option<&'static str>, LevelZeroInit, Option<SysmanRoute>)> =
OnceCell::new();
fn record_init(loaded_path: Option<&'static str>, init: LevelZeroInit, route: Option<SysmanRoute>) {
let _ = LZ_INIT_RECORD.set((loaded_path, init, route));
}
pub fn probe() -> LevelZeroProbe {
let device_bdfs = super::enumerated_pci_bdfs();
let (loaded_path, init, sysman_route) =
LZ_INIT_RECORD
.get()
.copied()
.unwrap_or((None, LevelZeroInit::LoaderMissing, None));
LevelZeroProbe {
compiled_in: true,
searched_paths: LIBZE_PATHS,
loaded_path,
init,
sysman_route,
device_count: device_bdfs.len(),
device_bdfs,
}
}
pub(crate) struct LzRuntime {
_library: Library,
pub(crate) api: LzApi,
pub(crate) devices_by_pci: HashMap<String, zes_device_handle_t_send>,
}
unsafe impl Send for LzRuntime {}
unsafe impl Sync for LzRuntime {}
#[derive(Clone, Copy)]
pub(crate) struct zes_device_handle_t_send(pub(crate) ffi::zes_device_handle_t);
unsafe impl Send for zes_device_handle_t_send {}
unsafe impl Sync for zes_device_handle_t_send {}
impl std::fmt::Debug for zes_device_handle_t_send {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("zes_device_handle_t_send")
.field(&(self.0 as usize))
.finish()
}
}
pub(crate) fn ensure_runtime() -> Option<&'static Mutex<Option<LzRuntime>>> {
Some(LZ_RUNTIME.get_or_init(|| Mutex::new(initialize_runtime())))
}
pub(crate) fn with_runtime<R>(f: impl FnOnce(&LzRuntime) -> R) -> Option<R> {
let lock = ensure_runtime()?;
let guard = lock.lock().ok()?;
let runtime = guard.as_ref()?;
Some(f(runtime))
}
fn initialize_runtime() -> Option<LzRuntime> {
let mut loaded: Option<LoadedLibrary> = None;
let mut loaded_path: Option<&'static str> = None;
for path in LIBZE_PATHS {
if let Some(lib) = unsafe { try_load_library(path) } {
debug!("Level Zero: loaded {path}");
loaded = Some(lib);
loaded_path = Some(path);
break;
}
}
let Some(loaded) = loaded else {
record_init(None, LevelZeroInit::LoaderMissing, None);
return None;
};
let api = loaded.api;
let sysman_env_enabled = std::env::var(SYSMAN_ENV_KEY)
.map(|v| v == "1")
.unwrap_or(false);
if api.zes_init.is_none() && !sysman_env_enabled {
debug!(
"Level Zero: loader does not expose zesInit and {SYSMAN_ENV_KEY}=1 was not set before zeInit; degrading"
);
record_init(loaded_path, LevelZeroInit::SysmanUnavailable, None);
return None;
}
let init_res = unsafe { (api.ze_init)(ffi::ZE_INIT_FLAG_DEFAULT) };
if init_res != ffi::ZE_RESULT_SUCCESS {
debug!("Level Zero: zeInit returned {init_res}; degrading");
record_init(loaded_path, LevelZeroInit::ZeInitFailed(init_res), None);
return None;
}
if let Some(zes_init) = api.zes_init {
let sysman_res = unsafe { (zes_init)(ffi::ZE_INIT_FLAG_DEFAULT) };
if sysman_res != ffi::ZE_RESULT_SUCCESS {
debug!("Level Zero: zesInit returned {sysman_res}; degrading");
record_init(loaded_path, LevelZeroInit::ZesInitFailed(sysman_res), None);
return None;
}
}
let route = if api.zes_init.is_some() {
SysmanRoute::ZesInit
} else {
SysmanRoute::LegacyEnvVar
};
record_init(loaded_path, LevelZeroInit::Ok, Some(route));
let devices_by_pci = enumerate_devices(&api);
if devices_by_pci.is_empty() {
debug!("Level Zero: zeInit succeeded but no devices visible to L0");
}
Some(LzRuntime {
_library: loaded.library,
api,
devices_by_pci,
})
}
fn enumerate_devices(api: &LzApi) -> HashMap<String, zes_device_handle_t_send> {
let mut out = HashMap::new();
let mut driver_count: u32 = 0;
let r = unsafe { (api.ze_driver_get)(&mut driver_count, std::ptr::null_mut()) };
if r != ffi::ZE_RESULT_SUCCESS || driver_count == 0 {
debug!("Level Zero: zeDriverGet returned {r}, count {driver_count}");
return out;
}
let (drivers_cap, mut driver_count) = cap_handle_count(driver_count, "drivers");
let mut drivers: Vec<ffi::ze_driver_handle_t> =
vec![std::ptr::null_mut::<c_void>(); drivers_cap];
let r = unsafe { (api.ze_driver_get)(&mut driver_count, drivers.as_mut_ptr()) };
if r != ffi::ZE_RESULT_SUCCESS {
debug!("Level Zero: zeDriverGet (fill) returned {r}");
return out;
}
drivers.truncate((driver_count as usize).min(drivers_cap));
for driver in drivers.iter().copied() {
if driver.is_null() {
continue;
}
let mut dev_count: u32 = 0;
let r = unsafe { (api.ze_device_get)(driver, &mut dev_count, std::ptr::null_mut()) };
if r != ffi::ZE_RESULT_SUCCESS || dev_count == 0 {
continue;
}
let (devices_cap, mut dev_count) = cap_handle_count(dev_count, "devices");
let mut devices: Vec<ffi::ze_device_handle_t> =
vec![std::ptr::null_mut::<c_void>(); devices_cap];
let r = unsafe { (api.ze_device_get)(driver, &mut dev_count, devices.as_mut_ptr()) };
if r != ffi::ZE_RESULT_SUCCESS {
continue;
}
devices.truncate((dev_count as usize).min(devices_cap));
for device in devices.iter().copied() {
if device.is_null() {
continue;
}
let mut props = ffi::zes_pci_properties_t::default();
let r = unsafe { (api.zes_device_pci_get_properties)(device, &mut props) };
if r != ffi::ZE_RESULT_SUCCESS {
continue;
}
let bdf = format_pci_bdf(&props.address);
out.insert(bdf, zes_device_handle_t_send(device));
}
}
out
}
pub(crate) fn format_pci_bdf(addr: &ffi::zes_pci_address_t) -> String {
format!(
"{:04x}:{:02x}:{:02x}.{:x}",
addr.domain, addr.bus, addr.device, addr.function
)
}
pub fn normalise_pci_bdf(raw: &str) -> String {
raw.to_ascii_lowercase()
}
#[cfg(test)]
pub(crate) fn install_test_runtime(_map: HashMap<String, zes_device_handle_t_send>) {
}