1use super::slot;
6use super::{TelemetryDecodeCapacityEvidence, TELEMETRY_DECODE_CAPACITY_SCHEMA_VERSION};
7use rustc_hash::FxHashMap;
8
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum RingStatus {
13 Empty,
15 Published,
17 Claimed,
19 Done,
21 WaitIo,
23 Yield,
25 Requeue,
27 Fault,
29 Unknown(u32),
31}
32
33impl RingStatus {
34 #[must_use]
35 pub(super) fn from_raw(raw: u32) -> Self {
36 match raw {
37 slot::EMPTY => Self::Empty,
38 slot::PUBLISHED => Self::Published,
39 slot::CLAIMED => Self::Claimed,
40 slot::DONE => Self::Done,
41 slot::WAIT_IO => Self::WaitIo,
42 slot::YIELD => Self::Yield,
43 slot::REQUEUE => Self::Requeue,
44 slot::FAULT => Self::Fault,
45 other => Self::Unknown(other),
46 }
47 }
48
49 #[must_use]
51 pub const fn raw(self) -> u32 {
52 match self {
53 Self::Empty => slot::EMPTY,
54 Self::Published => slot::PUBLISHED,
55 Self::Claimed => slot::CLAIMED,
56 Self::Done => slot::DONE,
57 Self::WaitIo => slot::WAIT_IO,
58 Self::Yield => slot::YIELD,
59 Self::Requeue => slot::REQUEUE,
60 Self::Fault => slot::FAULT,
61 Self::Unknown(raw) => raw,
62 }
63 }
64
65 #[must_use]
68 pub const fn is_active(self) -> bool {
69 matches!(
70 self,
71 Self::Published | Self::Claimed | Self::WaitIo | Self::Yield | Self::Requeue
72 )
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct RingSlotSnapshot {
79 pub slot_idx: u32,
81 pub status: RingStatus,
83 pub tenant_id: u32,
85 pub opcode: u32,
87 pub args_prefix: [u32; 3],
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct WindowTelemetry {
94 pub ticket: u32,
96 pub tenant_id: u32,
98 pub opcode: u32,
100 pub required_slots: u32,
102 pub lookahead_slots: u32,
104 pub published: u32,
106 pub claimed: u32,
108 pub done: u32,
110 pub wait_io: u32,
112 pub yield_count: u32,
114 pub requeue: u32,
116 pub fault: u32,
118}
119
120impl WindowTelemetry {
121 #[must_use]
123 pub const fn is_active(&self) -> bool {
124 self.published > 0
125 || self.claimed > 0
126 || self.wait_io > 0
127 || self.yield_count > 0
128 || self.requeue > 0
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
134pub struct RingOccupancy {
135 pub empty: u32,
137 pub published: u32,
139 pub claimed: u32,
141 pub done: u32,
143 pub wait_io: u32,
145 pub yield_count: u32,
147 pub requeue: u32,
149 pub fault: u32,
151 pub unknown: u32,
153}
154
155impl RingOccupancy {
156 #[must_use]
158 pub fn total_slots(&self) -> u32 {
159 checked_status_sum(
160 [
161 self.empty,
162 self.published,
163 self.claimed,
164 self.done,
165 self.wait_io,
166 self.yield_count,
167 self.requeue,
168 self.fault,
169 self.unknown,
170 ],
171 "total ring slots",
172 )
173 }
174
175 #[must_use]
177 pub fn queue_depth(&self) -> u32 {
178 checked_status_sum(
179 [
180 self.published,
181 self.claimed,
182 self.wait_io,
183 self.yield_count,
184 self.requeue,
185 self.fault,
186 self.unknown,
187 ],
188 "ring queue depth",
189 )
190 }
191}
192
193
194#[derive(Debug, Clone, PartialEq, Eq, Default)]
196pub struct ControlSnapshot {
197 pub shutdown: bool,
199 pub done_count: u32,
201 pub epoch: u32,
203 pub metrics: Vec<(u32, u32)>,
205 pub tenant_fairness: Vec<u32>,
207 pub priority_fairness: Vec<u32>,
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213pub struct ResidentRuntimeCounters {
214 pub total_slots: u32,
216 pub queue_depth: u32,
218 pub gpu_idle_slots: u32,
220 pub gpu_idle_ppm: u32,
222 pub frontier_density_bps: u16,
224 pub occupancy_proxy_bps: u16,
226 pub drained_slots: u32,
228 pub unreclaimed_done_slots: u32,
230 pub tenant_fairness_total: u64,
232 pub tenant_fairness_skew: u32,
234 pub priority_fairness_total: u64,
236 pub requeue_slots: u32,
238 pub fault_slots: u32,
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
244pub struct ResidentWatchdogSnapshot {
245 pub done_delta: u32,
247 pub queue_depth: u32,
249 pub fault_slots: u32,
251 pub requeue_slots: u32,
253 pub gpu_idle_ppm: u32,
255 pub suspected_stall: bool,
257}
258
259#[derive(Debug, Clone, PartialEq, Eq, Default)]
261pub struct RingTelemetry {
262 pub control: ControlSnapshot,
264 pub occupancy: RingOccupancy,
266 pub slots: Vec<RingSlotSnapshot>,
268 pub windows: Vec<WindowTelemetry>,
270}
271
272impl RingTelemetry {
273 #[must_use]
275 pub fn decode_capacity_evidence(
276 &self,
277 scratch: &TelemetryDecodeScratch,
278 ) -> TelemetryDecodeCapacityEvidence {
279 TelemetryDecodeCapacityEvidence {
280 schema_version: TELEMETRY_DECODE_CAPACITY_SCHEMA_VERSION,
281 decoded_slot_count: self.slots.len(),
282 slot_output_capacity: self.slots.capacity(),
283 decoded_window_count: self.windows.len(),
284 window_output_capacity: self.windows.capacity(),
285 window_opcode_scratch_capacity: scratch.window_opcodes.capacity(),
286 window_accumulator_scratch_capacity: scratch.windows.capacity(),
287 uses_caller_owned_scratch: true,
288 }
289 }
290}
291
292#[derive(Debug, Default)]
298pub struct TelemetryDecodeScratch {
299 pub(super) window_opcodes: Vec<u32>,
300 pub(super) windows: FxHashMap<(u32, u32), WindowAccumulator>,
301}
302
303impl TelemetryDecodeScratch {
304 #[must_use]
306 pub fn new() -> Self {
307 Self {
308 window_opcodes: Vec::new(),
309 windows: FxHashMap::default(),
310 }
311 }
312
313 pub fn clear(&mut self) {
315 self.window_opcodes.clear();
316 self.windows.clear();
317 }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
321pub(super) struct WindowAccumulator {
322 pub(super) tenant_id: u32,
323 pub(super) opcode: u32,
324 pub(super) required_slots: u32,
325 pub(super) lookahead_slots: u32,
326 pub(super) published: u32,
327 pub(super) claimed: u32,
328 pub(super) done: u32,
329 pub(super) wait_io: u32,
330 pub(super) yield_count: u32,
331 pub(super) requeue: u32,
332 pub(super) fault: u32,
333}
334
335pub(super) fn checked_status_sum<const N: usize>(values: [u32; N], label: &'static str) -> u32 {
336 let _ = label;
337 values
338 .into_iter()
339 .fold(0_u32, |acc, value| acc.saturating_add(value))
340}