1use ax_sync::SpinLock;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum InterruptTransition {
12 None,
14 Assert,
16 Deassert,
18}
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub struct InterruptReadOutcome {
23 pub value: u8,
25 pub transition: InterruptTransition,
27}
28
29#[derive(Default)]
30struct InterruptState {
31 isr: u8,
32 asserted: bool,
33 disabled: bool,
34 needs_resync: bool,
35 transition_in_flight: Option<bool>,
36}
37
38pub struct VirtioPciInterruptCoordinator {
40 state: SpinLock<InterruptState>,
41}
42
43impl VirtioPciInterruptCoordinator {
44 pub const fn new() -> Self {
46 Self {
47 state: SpinLock::new(InterruptState {
48 isr: 0,
49 asserted: false,
50 disabled: false,
51 needs_resync: false,
52 transition_in_flight: None,
53 }),
54 }
55 }
56
57 pub fn pending(&self) -> bool {
59 self.state.lock().isr != 0
60 }
61
62 pub fn asserted(&self) -> bool {
64 self.state.lock().asserted
65 }
66
67 pub fn needs_resync(&self) -> bool {
69 self.state.lock().needs_resync
70 }
71
72 pub fn record_queue_completion(&self, notify: bool) -> InterruptTransition {
74 self.record(1, notify)
75 }
76
77 pub fn record_config_change(&self) -> InterruptTransition {
79 self.record(2, true)
80 }
81
82 pub fn suppress_queue_completion(
86 &self,
87 transition: InterruptTransition,
88 ) -> InterruptTransition {
89 let mut state = self.state.lock();
90 state.isr &= !1;
91 let target = match transition {
92 InterruptTransition::Assert => Some(true),
93 InterruptTransition::Deassert => Some(false),
94 InterruptTransition::None => None,
95 };
96 if target.is_some() && state.transition_in_flight == target {
97 state.transition_in_flight = None;
101 let desired = !state.disabled && state.isr != 0;
102 state.needs_resync = state.asserted != desired;
103 return InterruptTransition::None;
104 }
105 requested_transition(&mut state)
106 }
107
108 pub fn read_isr(&self) -> InterruptReadOutcome {
110 let mut state = self.state.lock();
111 let value = state.isr;
112 state.isr = 0;
113 let transition = requested_transition(&mut state);
114 InterruptReadOutcome { value, transition }
115 }
116
117 pub fn set_disabled(&self, disabled: bool) -> InterruptTransition {
119 let mut state = self.state.lock();
120 state.disabled = disabled;
121 requested_transition(&mut state)
122 }
123
124 pub fn complete_transition(
131 &self,
132 transition: InterruptTransition,
133 success: bool,
134 ) -> InterruptTransition {
135 if transition == InterruptTransition::None {
136 return InterruptTransition::None;
137 }
138 let mut state = self.state.lock();
139 let target = match transition {
140 InterruptTransition::Assert => true,
141 InterruptTransition::Deassert => false,
142 InterruptTransition::None => return InterruptTransition::None,
143 };
144 if state.transition_in_flight != Some(target) {
145 return InterruptTransition::None;
146 }
147 state.transition_in_flight = None;
148 if success {
149 state.asserted = target;
150 state.needs_resync = false;
151 requested_transition(&mut state)
152 } else {
153 state.needs_resync = true;
154 InterruptTransition::None
155 }
156 }
157
158 pub fn suppress_transition(&self, transition: InterruptTransition) {
160 if transition == InterruptTransition::None {
161 return;
162 }
163 let mut state = self.state.lock();
164 let target = matches!(transition, InterruptTransition::Assert);
165 if state.transition_in_flight == Some(target) {
166 state.transition_in_flight = None;
167 let desired = !state.disabled && state.isr != 0;
168 state.needs_resync = state.asserted != desired;
169 }
170 }
171
172 pub(super) fn suppress_stale_transition(&self, transition: InterruptTransition) {
177 if transition == InterruptTransition::None {
178 return;
179 }
180 let mut state = self.state.lock();
181 let target = matches!(transition, InterruptTransition::Assert);
182 if state.transition_in_flight == Some(target) {
183 state.transition_in_flight = None;
184 }
185 }
186
187 pub fn cancel_transition(&self, transition: InterruptTransition) {
191 if transition == InterruptTransition::None {
192 return;
193 }
194 let mut state = self.state.lock();
195 let target = matches!(transition, InterruptTransition::Assert);
196 if state.transition_in_flight == Some(target) {
197 state.transition_in_flight = None;
198 let desired = !state.disabled && state.isr != 0;
199 state.needs_resync = state.asserted != desired;
200 }
201 }
202
203 pub fn resynchronize(&self) -> InterruptTransition {
205 let mut state = self.state.lock();
206 requested_transition(&mut state)
207 }
208
209 pub fn reset(&self) -> InterruptTransition {
211 let mut state = self.state.lock();
212 let transition = if state.asserted || state.needs_resync {
213 InterruptTransition::Deassert
214 } else {
215 InterruptTransition::None
216 };
217 *state = InterruptState {
218 disabled: state.disabled,
219 asserted: state.asserted,
220 ..InterruptState::default()
221 };
222 if transition == InterruptTransition::Deassert {
223 state.transition_in_flight = Some(false);
227 }
228 transition
229 }
230
231 fn record(&self, bit: u8, notify: bool) -> InterruptTransition {
232 let mut state = self.state.lock();
233 state.isr |= bit;
234 if notify {
235 requested_transition(&mut state)
236 } else {
237 InterruptTransition::None
238 }
239 }
240}
241
242fn requested_transition(state: &mut InterruptState) -> InterruptTransition {
243 if state.transition_in_flight.is_some() {
244 return InterruptTransition::None;
245 }
246 let desired = !state.disabled && state.isr != 0;
247 if desired == state.asserted {
248 state.needs_resync = false;
249 return InterruptTransition::None;
250 }
251 state.transition_in_flight = Some(desired);
252 state.needs_resync = false;
253 if desired {
254 InterruptTransition::Assert
255 } else {
256 InterruptTransition::Deassert
257 }
258}
259
260impl Default for VirtioPciInterruptCoordinator {
261 fn default() -> Self {
262 Self::new()
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn notification_is_suppressed_until_interrupts_are_enabled() {
272 let coordinator = VirtioPciInterruptCoordinator::new();
273 assert_eq!(coordinator.set_disabled(true), InterruptTransition::None);
274 assert_eq!(
275 coordinator.record_queue_completion(true),
276 InterruptTransition::None
277 );
278 assert!(coordinator.pending());
279 assert_eq!(coordinator.set_disabled(false), InterruptTransition::Assert);
280 let assert_transition = coordinator.resynchronize();
281 assert_eq!(assert_transition, InterruptTransition::None);
282 coordinator.complete_transition(InterruptTransition::Assert, true);
283 assert_eq!(
284 coordinator.read_isr(),
285 InterruptReadOutcome {
286 value: 1,
287 transition: InterruptTransition::Deassert,
288 }
289 );
290 coordinator.complete_transition(InterruptTransition::Deassert, true);
291 }
292
293 #[test]
294 fn failed_line_transition_is_retryable_without_losing_isr_state() {
295 let coordinator = VirtioPciInterruptCoordinator::new();
296 let assert_transition = coordinator.record_config_change();
297 assert_eq!(assert_transition, InterruptTransition::Assert);
298 coordinator.complete_transition(assert_transition, false);
299 assert!(coordinator.needs_resync());
300 let retry_assert = coordinator.resynchronize();
301 assert_eq!(retry_assert, InterruptTransition::Assert);
302 coordinator.complete_transition(retry_assert, true);
303
304 let read = coordinator.read_isr();
305 assert_eq!(read.value, 2);
306 coordinator.complete_transition(read.transition, false);
307 let deassert = coordinator.resynchronize();
308 assert_eq!(deassert, InterruptTransition::Deassert);
309 coordinator.complete_transition(deassert, true);
310 assert!(!coordinator.needs_resync());
311 }
312
313 #[test]
314 fn stale_queue_suppression_preserves_config_change() {
315 let coordinator = VirtioPciInterruptCoordinator::new();
316 assert_eq!(
317 coordinator.record_config_change(),
318 InterruptTransition::Assert
319 );
320 assert_eq!(
321 coordinator.record_queue_completion(true),
322 InterruptTransition::None
323 );
324 assert_eq!(
325 coordinator.suppress_queue_completion(InterruptTransition::None),
326 InterruptTransition::None
327 );
328 assert_eq!(coordinator.read_isr().value, 2);
329 }
330
331 #[test]
332 fn stale_queue_suppression_releases_a_pending_deassertion() {
333 let coordinator = VirtioPciInterruptCoordinator::new();
334 let assert_transition = coordinator.record_config_change();
335 coordinator.complete_transition(assert_transition, true);
336 assert_eq!(
337 coordinator.set_disabled(true),
338 InterruptTransition::Deassert
339 );
340 coordinator.complete_transition(InterruptTransition::Deassert, false);
341
342 assert_eq!(
343 coordinator.record_queue_completion(true),
344 InterruptTransition::Deassert
345 );
346 assert_eq!(
347 coordinator.suppress_queue_completion(InterruptTransition::Deassert),
348 InterruptTransition::None
349 );
350 assert_eq!(coordinator.resynchronize(), InterruptTransition::Deassert);
351 }
352
353 #[test]
354 fn stale_queue_assert_suppression_releases_config_assertion() {
355 let coordinator = VirtioPciInterruptCoordinator::new();
356 assert_eq!(
357 coordinator.record_queue_completion(true),
358 InterruptTransition::Assert
359 );
360 assert_eq!(
361 coordinator.record_config_change(),
362 InterruptTransition::None
363 );
364 assert_eq!(
365 coordinator.suppress_queue_completion(InterruptTransition::Assert),
366 InterruptTransition::None
367 );
368 assert_eq!(coordinator.resynchronize(), InterruptTransition::Assert);
369 }
370
371 #[test]
372 fn completion_reconciles_a_read_that_raced_with_assertion() {
373 let coordinator = VirtioPciInterruptCoordinator::new();
374 let assert_transition = coordinator.record_queue_completion(true);
375 assert_eq!(assert_transition, InterruptTransition::Assert);
376 let read = coordinator.read_isr();
377 assert_eq!(read.value, 1);
378 assert_eq!(read.transition, InterruptTransition::None);
379
380 let deassert_transition = coordinator.complete_transition(assert_transition, true);
381 assert_eq!(deassert_transition, InterruptTransition::Deassert);
382 coordinator.complete_transition(deassert_transition, true);
383 assert!(!coordinator.asserted());
384 }
385
386 #[test]
387 fn reset_deassertion_remains_retryable_after_a_line_failure() {
388 let coordinator = VirtioPciInterruptCoordinator::new();
389 let assert_transition = coordinator.record_config_change();
390 coordinator.complete_transition(assert_transition, true);
391
392 let reset_transition = coordinator.reset();
393 assert_eq!(reset_transition, InterruptTransition::Deassert);
394 coordinator.complete_transition(reset_transition, false);
395 assert!(coordinator.needs_resync());
396
397 let retry = coordinator.resynchronize();
398 assert_eq!(retry, InterruptTransition::Deassert);
399 coordinator.complete_transition(retry, true);
400 assert!(!coordinator.needs_resync());
401 }
402
403 #[test]
404 fn stale_transition_suppression_preserves_existing_retry_state() {
405 let coordinator = VirtioPciInterruptCoordinator::new();
406 let assert_transition = coordinator.record_queue_completion(true);
407 assert_eq!(assert_transition, InterruptTransition::Assert);
408 coordinator.complete_transition(assert_transition, false);
409 assert!(coordinator.needs_resync());
410
411 coordinator.suppress_stale_transition(InterruptTransition::Assert);
412 assert!(coordinator.needs_resync());
413 }
414}