Skip to content

Commit 0039432

Browse files
committed
fix(xgmi): retry failed link-speed reads instead of caching the failure
1 parent ddeef41 commit 0039432

1 file changed

Lines changed: 31 additions & 13 deletions

File tree

src/xgmi.rs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use std::ffi::CStr;
66
use std::io;
77
use std::ptr;
8-
use std::sync::OnceLock;
8+
use std::sync::{Mutex, OnceLock};
99

1010
/// Snapshot of a single XGMI link at one sample.
1111
#[derive(Clone, Debug)]
@@ -255,7 +255,7 @@ pub fn read_all_xgmi_stats() -> io::Result<Vec<XgmiSnapshot>> {
255255

256256
let mut snapshots = Vec::with_capacity(handles.len());
257257
for idx in 0..handles.len() {
258-
if let Some(snap) = read_processor_snapshot(api, &handles, &bdfs, names, speeds, idx) {
258+
if let Some(snap) = read_processor_snapshot(api, &handles, &bdfs, names, &speeds, idx) {
259259
snapshots.push(snap);
260260
}
261261
}
@@ -275,15 +275,33 @@ fn gpu_names(api: &Amdsmi, handles: &[ffi::ProcessorHandle]) -> &'static [String
275275
})
276276
}
277277

278-
/// Per-GPU (bit_rate, bandwidth) Gb/s, read once and cached by GPU index:
279-
/// link speed is static and `amdsmi_get_pcie_info` costs ~0.5 ms per call —
280-
/// roughly half of the whole refresh when polled for every GPU each second.
281-
fn link_speeds(
282-
api: &Amdsmi,
283-
handles: &[ffi::ProcessorHandle],
284-
) -> &'static [(Option<f64>, Option<f64>)] {
285-
static SPEEDS: OnceLock<Vec<(Option<f64>, Option<f64>)>> = OnceLock::new();
286-
SPEEDS.get_or_init(|| handles.iter().map(|&h| read_link_speed(api, h)).collect())
278+
/// Per-link (bit_rate, bandwidth) in Gb/s from PCIe static info.
279+
type LinkSpeed = (Option<f64>, Option<f64>);
280+
281+
/// Per-GPU link speeds. Link speed is static and `amdsmi_get_pcie_info`
282+
/// costs ~0.5 ms (two SMU round-trips), so successful reads are cached by
283+
/// GPU index; failed reads retry on the next poll rather than degrading
284+
/// speed reporting for the process lifetime.
285+
fn link_speeds(api: &Amdsmi, handles: &[ffi::ProcessorHandle]) -> Vec<LinkSpeed> {
286+
static CACHE: Mutex<Vec<Option<LinkSpeed>>> = Mutex::new(Vec::new());
287+
let mut cache = CACHE.lock().unwrap_or_else(|e| e.into_inner());
288+
if cache.len() < handles.len() {
289+
cache.resize(handles.len(), None);
290+
}
291+
handles
292+
.iter()
293+
.zip(cache.iter_mut())
294+
.map(|(&handle, slot)| match slot {
295+
Some(speed) => *speed,
296+
None => {
297+
let speed = read_link_speed(api, handle);
298+
if speed != (None, None) {
299+
*slot = Some(speed);
300+
}
301+
speed
302+
}
303+
})
304+
.collect()
287305
}
288306

289307
/// Flatten socket/processor enumeration into one ordered GPU handle list.
@@ -331,7 +349,7 @@ fn read_processor_snapshot(
331349
handles: &[ffi::ProcessorHandle],
332350
bdfs: &[Option<u64>],
333351
names: &[String],
334-
speeds: &[(Option<f64>, Option<f64>)],
352+
speeds: &[LinkSpeed],
335353
idx: usize,
336354
) -> Option<XgmiSnapshot> {
337355
let handle = handles[idx];
@@ -408,7 +426,7 @@ fn is_xgmi_peer(api: &Amdsmi, src: ffi::ProcessorHandle, dst: ffi::ProcessorHand
408426

409427
/// Per-link (bit_rate, bandwidth) in Gb/s from PCIe static info, mirroring
410428
/// amd-smi: bit_rate = max_pcie_speed/1000, bandwidth = bit_rate * width.
411-
fn read_link_speed(api: &Amdsmi, handle: ffi::ProcessorHandle) -> (Option<f64>, Option<f64>) {
429+
fn read_link_speed(api: &Amdsmi, handle: ffi::ProcessorHandle) -> LinkSpeed {
412430
let mut info: Padded<ffi::PcieInfo> = Padded::zeroed();
413431
let rc = unsafe { (api.get_pcie_info)(handle, &mut info.value) };
414432
if rc != ffi::AMDSMI_STATUS_SUCCESS || info.value.max_pcie_speed == 0 {

0 commit comments

Comments
 (0)